Loading drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +202 −5 Original line number Diff line number Diff line Loading @@ -320,7 +320,8 @@ static void rgmii_dump(struct qcom_ethqos *ethqos) #define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ (5 * 1000 * 1000UL) static void ethqos_update_rgmii_clk(struct qcom_ethqos *ethqos, unsigned int speed) ethqos_update_rgmii_clk_and_bus_cfg(struct qcom_ethqos *ethqos, unsigned int speed) { switch (speed) { case SPEED_1000: Loading @@ -336,6 +337,21 @@ ethqos_update_rgmii_clk(struct qcom_ethqos *ethqos, unsigned int speed) break; } switch (speed) { case SPEED_1000: ethqos->vote_idx = VOTE_IDX_1000MBPS; break; case SPEED_100: ethqos->vote_idx = VOTE_IDX_100MBPS; break; case SPEED_10: ethqos->vote_idx = VOTE_IDX_10MBPS; break; case 0: ethqos->vote_idx = VOTE_IDX_0MBPS; ethqos->rgmii_clk_rate = 0; break; } clk_set_rate(ethqos->rgmii_clk, ethqos->rgmii_clk_rate); } Loading Loading @@ -665,7 +681,7 @@ static void ethqos_fix_mac_speed(void *priv, unsigned int speed) struct qcom_ethqos *ethqos = priv; ethqos->speed = speed; ethqos_update_rgmii_clk(ethqos, speed); ethqos_update_rgmii_clk_and_bus_cfg(ethqos, speed); ethqos_configure(ethqos); } Loading Loading @@ -741,6 +757,13 @@ static void ethqos_handle_phy_interrupt(struct qcom_ethqos *ethqos) ETHQOSDBG("MICREL PHY Intr EN Reg (%#x) = %#x\n", DWC_ETH_QOS_MICREL_PHY_INTCS, micrel_intr_status); /** * Call ack interrupt to clear the WOL * interrupt status fields */ if (priv->phydev->drv->ack_interrupt) priv->phydev->drv->ack_interrupt(priv->phydev); /* Interrupt received for link state change */ if (phy_intr_status & LINK_STATE_MASK) { if (micrel_intr_status & MICREL_LINK_UP_INTR_STATUS) Loading Loading @@ -769,6 +792,9 @@ static void ethqos_defer_phy_isr_work(struct work_struct *work) struct qcom_ethqos *ethqos = container_of(work, struct qcom_ethqos, emac_phy_work); if (ethqos->clks_suspended) wait_for_completion(ðqos->clk_enable_done); ethqos_handle_phy_interrupt(ethqos); } Loading @@ -776,6 +802,8 @@ static irqreturn_t ETHQOS_PHY_ISR(int irq, void *dev_data) { struct qcom_ethqos *ethqos = (struct qcom_ethqos *)dev_data; pm_wakeup_event(ðqos->pdev->dev, 5000); queue_work(system_wq, ðqos->emac_phy_work); return IRQ_HANDLED; } Loading @@ -785,6 +813,8 @@ static int ethqos_phy_intr_enable(struct qcom_ethqos *ethqos) int ret = 0; INIT_WORK(ðqos->emac_phy_work, ethqos_defer_phy_isr_work); init_completion(ðqos->clk_enable_done); ret = request_irq(ethqos->phy_intr, ETHQOS_PHY_ISR, IRQF_SHARED, "stmmac", ethqos); if (ret) { Loading Loading @@ -867,6 +897,106 @@ static void ethqos_pps_irq_config(struct qcom_ethqos *ethqos) } } static void qcom_ethqos_phy_suspend_clks(struct qcom_ethqos *ethqos) { ETHQOSINFO("Enter\n"); if (phy_intr_en) reinit_completion(ðqos->clk_enable_done); ethqos->clks_suspended = 1; ethqos_update_rgmii_clk_and_bus_cfg(ethqos, 0); ETHQOSINFO("Exit\n"); } inline void *qcom_ethqos_get_priv(struct qcom_ethqos *ethqos) { struct platform_device *pdev = ethqos->pdev; struct net_device *dev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(dev); return priv; } inline bool qcom_ethqos_is_phy_link_up(struct qcom_ethqos *ethqos) { /* PHY driver initializes phydev->link=1. * So, phydev->link is 1 even on bootup with no PHY connected. * phydev->link is valid only after adjust_link is called once. */ struct stmmac_priv *priv = qcom_ethqos_get_priv(ethqos); return (priv->dev->phydev && priv->dev->phydev->link); } static void qcom_ethqos_phy_resume_clks(struct qcom_ethqos *ethqos) { ETHQOSINFO("Enter\n"); if (qcom_ethqos_is_phy_link_up(ethqos)) ethqos_update_rgmii_clk_and_bus_cfg(ethqos, ethqos->speed); else ethqos_update_rgmii_clk_and_bus_cfg(ethqos, SPEED_10); ethqos->clks_suspended = 0; if (phy_intr_en) complete_all(ðqos->clk_enable_done); ETHQOSINFO("Exit\n"); } void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat) { struct qcom_ethqos *ethqos = plat->bsp_priv; struct platform_device *pdev = ethqos->pdev; struct net_device *ndev = platform_get_drvdata(pdev); ethqos->phy_wol_supported = 0; ethqos->phy_wol_wolopts = 0; /* Check if phydev is valid*/ /* Check and enable Wake-on-LAN functionality in PHY*/ if (ndev->phydev) { struct ethtool_wolinfo wol = {.cmd = ETHTOOL_GWOL}; wol.supported = 0; wol.wolopts = 0; ETHQOSINFO("phydev addr: 0x%pK\n", ndev->phydev); phy_ethtool_get_wol(ndev->phydev, &wol); ethqos->phy_wol_supported = wol.supported; ETHQOSINFO("Get WoL[0x%x] in %s\n", wol.supported, ndev->phydev->drv->name); /* Try to enable supported Wake-on-LAN features in PHY*/ if (wol.supported) { device_set_wakeup_capable(ðqos->pdev->dev, 1); wol.cmd = ETHTOOL_SWOL; wol.wolopts = wol.supported; if (!phy_ethtool_set_wol(ndev->phydev, &wol)) { ethqos->phy_wol_wolopts = wol.wolopts; enable_irq_wake(ethqos->phy_intr); device_set_wakeup_enable(ðqos->pdev->dev, 1); ETHQOSINFO("Enabled WoL[0x%x] in %s\n", wol.wolopts, ndev->phydev->drv->name); } else { ETHQOSINFO("Disabled WoL[0x%x] in %s\n", wol.wolopts, ndev->phydev->drv->name); } } else { ETHQOSINFO("WoL Not Supported\n"); } } } static int qcom_ethqos_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; Loading @@ -888,6 +1018,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_mem; } ethqos->pdev = pdev; ethqos_init_reqgulators(ethqos); Loading Loading @@ -920,8 +1051,8 @@ static int qcom_ethqos_probe(struct platform_device *pdev) if (ret) goto err_mem; ethqos->speed = SPEED_1000; ethqos_update_rgmii_clk(ethqos, SPEED_1000); ethqos->speed = SPEED_10; ethqos_update_rgmii_clk_and_bus_cfg(ethqos, SPEED_10); ethqos_set_func_clk_en(ethqos); plat_dat->bsp_priv = ethqos; Loading Loading @@ -1010,20 +1141,86 @@ static int qcom_ethqos_remove(struct platform_device *pdev) if (phy_intr_en) free_irq(ethqos->phy_intr, ethqos); if (phy_intr_en) cancel_work_sync(ðqos->emac_phy_work); emac_emb_smmu_exit(); ethqos_disable_regulators(ethqos); return ret; } static int qcom_ethqos_suspend(struct device *dev) { struct qcom_ethqos *ethqos; struct net_device *ndev = NULL; int ret; if (of_device_is_compatible(dev->of_node, "qcom,emac-smmu-embedded")) { ETHQOSDBG("smmu return\n"); return 0; } ethqos = get_stmmac_bsp_priv(dev); if (!ethqos) return -ENODEV; ndev = dev_get_drvdata(dev); if (!ndev || !netif_running(ndev)) return -EINVAL; ret = stmmac_suspend(dev); qcom_ethqos_phy_suspend_clks(ethqos); ETHQOSDBG(" ret = %d\n", ret); return ret; } static int qcom_ethqos_resume(struct device *dev) { struct net_device *ndev = NULL; struct qcom_ethqos *ethqos; int ret; ETHQOSDBG("Resume Enter\n"); if (of_device_is_compatible(dev->of_node, "qcom,emac-smmu-embedded")) return 0; ethqos = get_stmmac_bsp_priv(dev); if (!ethqos) return -ENODEV; ndev = dev_get_drvdata(dev); if (!ndev || !netif_running(ndev)) { ETHQOSERR(" Resume not possible\n"); return -EINVAL; } qcom_ethqos_phy_resume_clks(ethqos); ret = stmmac_resume(dev); ETHQOSDBG("<--Resume Exit\n"); return ret; } MODULE_DEVICE_TABLE(of, qcom_ethqos_match); static const struct dev_pm_ops qcom_ethqos_pm_ops = { .suspend = qcom_ethqos_suspend, .resume = qcom_ethqos_resume, }; static struct platform_driver qcom_ethqos_driver = { .probe = qcom_ethqos_probe, .remove = qcom_ethqos_remove, .driver = { .name = DRV_NAME, .pm = &stmmac_pltfr_pm_ops, .pm = &qcom_ethqos_pm_ops, .of_match_table = of_match_ptr(qcom_ethqos_match), }, }; Loading drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h +31 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ #ifndef _DWMAC_QCOM_ETHQOS_H #define _DWMAC_QCOM_ETHQOS_H //#include <linux/msm-bus.h> #define DRV_NAME "qcom-ethqos" #define ETHQOSDBG(fmt, args...) \ pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args) Loading Loading @@ -50,6 +52,11 @@ #define AVB_CLASS_A_CHANNEL_NUM 2 #define AVB_CLASS_B_CHANNEL_NUM 3 #define VOTE_IDX_0MBPS 0 #define VOTE_IDX_10MBPS 1 #define VOTE_IDX_100MBPS 2 #define VOTE_IDX_1000MBPS 3 static inline u32 PPSCMDX(u32 x, u32 val) { return (GENMASK(PPS_MINIDX(x) + 3, PPS_MINIDX(x)) & Loading @@ -67,6 +74,12 @@ static inline u32 PPSX_MASK(u32 x) return GENMASK(PPS_MAXIDX(x), PPS_MINIDX(x)); } enum IO_MACRO_PHY_MODE { RGMII_MODE, RMII_MODE, MII_MODE }; struct ethqos_emac_por { unsigned int offset; unsigned int value; Loading Loading @@ -94,9 +107,12 @@ struct qcom_ethqos { struct platform_device *pdev; void __iomem *rgmii_base; struct msm_bus_scale_pdata *bus_scale_vec; u32 bus_hdl; unsigned int rgmii_clk_rate; struct clk *rgmii_clk; unsigned int speed; unsigned int vote_idx; int gpio_phy_intr_redirect; u32 phy_intr; Loading Loading @@ -129,6 +145,17 @@ struct qcom_ethqos { unsigned long avb_class_a_intr_cnt; unsigned long avb_class_b_intr_cnt; /* saving state for Wake-on-LAN */ int wolopts; /* state of enabled wol options in PHY*/ u32 phy_wol_wolopts; /* state of supported wol options in PHY*/ u32 phy_wol_supported; /* Boolean to check if clock is suspended*/ int clks_suspended; /* Structure which holds done and wait members */ struct completion clk_enable_done; }; struct pps_cfg { Loading Loading @@ -164,6 +191,9 @@ int create_pps_interrupt_device_node(dev_t *pps_dev_t, struct cdev **pps_cdev, struct class **pps_class, char *pps_dev_node_name); bool qcom_ethqos_is_phy_link_up(struct qcom_ethqos *ethqos); void *qcom_ethqos_get_priv(struct qcom_ethqos *ethqos); int ppsout_config(struct stmmac_priv *priv, struct ifr_data_struct *req); u16 dwmac_qcom_select_queue(struct net_device *dev, Loading Loading @@ -221,4 +251,5 @@ struct dwmac_qcom_avb_algorithm { void dwmac_qcom_program_avb_algorithm(struct stmmac_priv *priv, struct ifr_data_struct *req); unsigned int dwmac_qcom_get_plat_tx_coal_frames(struct sk_buff *skb); void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat); #endif drivers/net/ethernet/stmicro/stmmac/stmmac.h +2 −0 Original line number Diff line number Diff line Loading @@ -264,6 +264,8 @@ int ethqos_handle_prv_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); int ethqos_init_pps(struct stmmac_priv *priv); extern bool phy_intr_en; void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat_dat); int stmmac_mdio_unregister(struct net_device *ndev); int stmmac_mdio_register(struct net_device *ndev); int stmmac_mdio_reset(struct mii_bus *mii); Loading drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +1 −16 Original line number Diff line number Diff line Loading @@ -1054,6 +1054,7 @@ static int stmmac_init_phy(struct net_device *dev) !priv->phydev->drv->config_intr(priv->phydev)) { pr_err(" qcom-ethqos: %s config_phy_intr successful aftre connect\n", __func__); qcom_ethqos_request_phy_wol(priv->plat); } } else { pr_info("stmmac phy polling mode\n"); Loading Loading @@ -4042,22 +4043,6 @@ static int stmmac_setup_tc(struct net_device *ndev, enum tc_setup_type type, } } static u16 stmmac_select_queue(struct net_device *dev, struct sk_buff *skb, struct net_device *sb_dev) { if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { /* * There is no way to determine the number of TSO * capable Queues. Let's use always the Queue 0 * because if TSO is supported then at least this * one will be capable. */ return 0; } return netdev_pick_tx(dev, skb, NULL) % dev->real_num_tx_queues; } static int stmmac_set_mac_address(struct net_device *ndev, void *addr) { struct stmmac_priv *priv = netdev_priv(ndev); Loading drivers/net/phy/micrel.c +139 −3 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <linux/of.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> /* Operation Mode Strap Override */ #define MII_KSZPHY_OMSO 0x16 Loading Loading @@ -72,6 +74,21 @@ #define PS_TO_REG 200 /*Register 2.10. 15:14 PME Output Select*/ #define MII_KSZPHY_OMSO_PME_N2 BIT(10) /*Register 2.10. BITS 6, 1 and 0 to detect the type of WOL */ #define MII_KSZPHY_WOL_MAGIC_PKT BIT(6) #define MII_KSZPHY_WOL_LINK_DOWN BIT(1) #define MII_KSZPHY_WOL_LINK_UP BIT(0) /* Register 2.10.15:14 PME Output Select */ #define MII_KSZPHY_WOL_CTRL_PME_N2 BIT(15) #define MII_KSZPHY_WOL_CTRL_INT_N BIT(14) /* MMD Address 2h, Register 2h Operation Mode Strap Override*/ #define MII_KSZPHY_OMSO_REG 0x2 /* MMD Address 2h, Register 10h Wake-On-LAN Control */ #define MII_KSZPHY_WOL_CTRL_REG 0x10 struct kszphy_hw_stat { const char *string; u8 reg; Loading Loading @@ -498,6 +515,32 @@ static int ksz9021_config_init(struct phy_device *phydev) #define MII_KSZ9031RN_EDPD 0x23 #define MII_KSZ9031RN_EDPD_ENABLE BIT(0) static int ksz9031_ack_interrupt(struct phy_device *phydev) { /* bit[7..0] int status, which is a read and clear register. */ int rc; u32 reg_value; rc = phy_read(phydev, MII_KSZPHY_INTCS); reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG); if (reg_value & MII_KSZPHY_OMSO_PME_N2) { /* PME output is cleared by disabling the PME trigger src */ reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value &= ~MII_KSZPHY_WOL_MAGIC_PKT; reg_value &= ~MII_KSZPHY_WOL_LINK_UP; reg_value &= ~MII_KSZPHY_WOL_LINK_DOWN; phy_write_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value |= MII_KSZPHY_WOL_MAGIC_PKT; reg_value |= MII_KSZPHY_WOL_LINK_UP; reg_value |= MII_KSZPHY_WOL_LINK_DOWN; phy_write_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); } return (rc < 0) ? rc : 0; } static int ksz9031_of_load_skew_values(struct phy_device *phydev, const struct device_node *of_node, u16 reg, size_t field_sz, Loading Loading @@ -984,6 +1027,97 @@ static int kszphy_probe(struct phy_device *phydev) return 0; } static void ksz9031_set_wol_settings(struct phy_device *phydev) { u32 reg_value; /* Enable both PHY and PME_N2 interrupts */ reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value |= MII_KSZPHY_WOL_CTRL_PME_N2; reg_value &= ~MII_KSZPHY_WOL_CTRL_INT_N; reg_value |= MII_KSZPHY_WOL_MAGIC_PKT; reg_value |= MII_KSZPHY_WOL_LINK_UP; reg_value |= MII_KSZPHY_WOL_LINK_DOWN; phy_write_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); } static int ksz9031_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { struct net_device *ndev = phydev->attached_dev; const u8 *mac; int ret = 0; u32 reg_value; if (!ndev) return -ENODEV; if (wol->wolopts & WAKE_MAGIC) { mac = (const u8 *)ndev->dev_addr; if (!is_valid_ether_addr(mac)) return -EINVAL; phy_write_mmd(phydev, 0x2, 0x11, mac[5] | (mac[4] << 8)); phy_write_mmd(phydev, 0x2, 0x12, mac[3] | (mac[2] << 8)); phy_write_mmd(phydev, 0x2, 0x13, mac[1] | (mac[0] << 8)); /* Enable WOL interrupt for magic pkt, link up and down */ ksz9031_set_wol_settings(phydev); /* Enable PME_N2 output */ reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG); reg_value |= MII_KSZPHY_OMSO_PME_N2; phy_write_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG, reg_value); } return ret; } static void ksz9031_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { u32 reg_value; wol->supported = WAKE_MAGIC; wol->wolopts = 0; reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG); if (reg_value & MII_KSZPHY_OMSO_PME_N2) wol->wolopts |= WAKE_MAGIC; } static int ksz9031_suspend(struct phy_device *phydev) { int value; int wol_enabled; u32 reg_value; reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG); wol_enabled = reg_value & MII_KSZPHY_OMSO_PME_N2; value = phy_read(phydev, MII_BMCR); if (wol_enabled) value |= BMCR_ISOLATE; else value |= BMCR_PDOWN; phy_write(phydev, MII_BMCR, value); return 0; } static int ksz9031_resume(struct phy_device *phydev) { int value; value = phy_read(phydev, MII_BMCR); value &= ~(BMCR_PDOWN | BMCR_ISOLATE); phy_write(phydev, MII_BMCR, value); return 0; } static struct phy_driver ksphy_driver[] = { { .phy_id = PHY_ID_KS8737, Loading Loading @@ -1139,13 +1273,15 @@ static struct phy_driver ksphy_driver[] = { .config_init = ksz9031_config_init, .soft_reset = genphy_soft_reset, .read_status = ksz9031_read_status, .ack_interrupt = kszphy_ack_interrupt, .ack_interrupt = ksz9031_ack_interrupt, .config_intr = kszphy_config_intr, .get_sset_count = kszphy_get_sset_count, .get_strings = kszphy_get_strings, .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = kszphy_resume, .set_wol = ksz9031_set_wol, .get_wol = ksz9031_get_wol, .suspend = ksz9031_suspend, .resume = ksz9031_resume, }, { .phy_id = PHY_ID_KSZ9131, .phy_id_mask = MICREL_PHY_ID_MASK, Loading Loading
drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +202 −5 Original line number Diff line number Diff line Loading @@ -320,7 +320,8 @@ static void rgmii_dump(struct qcom_ethqos *ethqos) #define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ (5 * 1000 * 1000UL) static void ethqos_update_rgmii_clk(struct qcom_ethqos *ethqos, unsigned int speed) ethqos_update_rgmii_clk_and_bus_cfg(struct qcom_ethqos *ethqos, unsigned int speed) { switch (speed) { case SPEED_1000: Loading @@ -336,6 +337,21 @@ ethqos_update_rgmii_clk(struct qcom_ethqos *ethqos, unsigned int speed) break; } switch (speed) { case SPEED_1000: ethqos->vote_idx = VOTE_IDX_1000MBPS; break; case SPEED_100: ethqos->vote_idx = VOTE_IDX_100MBPS; break; case SPEED_10: ethqos->vote_idx = VOTE_IDX_10MBPS; break; case 0: ethqos->vote_idx = VOTE_IDX_0MBPS; ethqos->rgmii_clk_rate = 0; break; } clk_set_rate(ethqos->rgmii_clk, ethqos->rgmii_clk_rate); } Loading Loading @@ -665,7 +681,7 @@ static void ethqos_fix_mac_speed(void *priv, unsigned int speed) struct qcom_ethqos *ethqos = priv; ethqos->speed = speed; ethqos_update_rgmii_clk(ethqos, speed); ethqos_update_rgmii_clk_and_bus_cfg(ethqos, speed); ethqos_configure(ethqos); } Loading Loading @@ -741,6 +757,13 @@ static void ethqos_handle_phy_interrupt(struct qcom_ethqos *ethqos) ETHQOSDBG("MICREL PHY Intr EN Reg (%#x) = %#x\n", DWC_ETH_QOS_MICREL_PHY_INTCS, micrel_intr_status); /** * Call ack interrupt to clear the WOL * interrupt status fields */ if (priv->phydev->drv->ack_interrupt) priv->phydev->drv->ack_interrupt(priv->phydev); /* Interrupt received for link state change */ if (phy_intr_status & LINK_STATE_MASK) { if (micrel_intr_status & MICREL_LINK_UP_INTR_STATUS) Loading Loading @@ -769,6 +792,9 @@ static void ethqos_defer_phy_isr_work(struct work_struct *work) struct qcom_ethqos *ethqos = container_of(work, struct qcom_ethqos, emac_phy_work); if (ethqos->clks_suspended) wait_for_completion(ðqos->clk_enable_done); ethqos_handle_phy_interrupt(ethqos); } Loading @@ -776,6 +802,8 @@ static irqreturn_t ETHQOS_PHY_ISR(int irq, void *dev_data) { struct qcom_ethqos *ethqos = (struct qcom_ethqos *)dev_data; pm_wakeup_event(ðqos->pdev->dev, 5000); queue_work(system_wq, ðqos->emac_phy_work); return IRQ_HANDLED; } Loading @@ -785,6 +813,8 @@ static int ethqos_phy_intr_enable(struct qcom_ethqos *ethqos) int ret = 0; INIT_WORK(ðqos->emac_phy_work, ethqos_defer_phy_isr_work); init_completion(ðqos->clk_enable_done); ret = request_irq(ethqos->phy_intr, ETHQOS_PHY_ISR, IRQF_SHARED, "stmmac", ethqos); if (ret) { Loading Loading @@ -867,6 +897,106 @@ static void ethqos_pps_irq_config(struct qcom_ethqos *ethqos) } } static void qcom_ethqos_phy_suspend_clks(struct qcom_ethqos *ethqos) { ETHQOSINFO("Enter\n"); if (phy_intr_en) reinit_completion(ðqos->clk_enable_done); ethqos->clks_suspended = 1; ethqos_update_rgmii_clk_and_bus_cfg(ethqos, 0); ETHQOSINFO("Exit\n"); } inline void *qcom_ethqos_get_priv(struct qcom_ethqos *ethqos) { struct platform_device *pdev = ethqos->pdev; struct net_device *dev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(dev); return priv; } inline bool qcom_ethqos_is_phy_link_up(struct qcom_ethqos *ethqos) { /* PHY driver initializes phydev->link=1. * So, phydev->link is 1 even on bootup with no PHY connected. * phydev->link is valid only after adjust_link is called once. */ struct stmmac_priv *priv = qcom_ethqos_get_priv(ethqos); return (priv->dev->phydev && priv->dev->phydev->link); } static void qcom_ethqos_phy_resume_clks(struct qcom_ethqos *ethqos) { ETHQOSINFO("Enter\n"); if (qcom_ethqos_is_phy_link_up(ethqos)) ethqos_update_rgmii_clk_and_bus_cfg(ethqos, ethqos->speed); else ethqos_update_rgmii_clk_and_bus_cfg(ethqos, SPEED_10); ethqos->clks_suspended = 0; if (phy_intr_en) complete_all(ðqos->clk_enable_done); ETHQOSINFO("Exit\n"); } void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat) { struct qcom_ethqos *ethqos = plat->bsp_priv; struct platform_device *pdev = ethqos->pdev; struct net_device *ndev = platform_get_drvdata(pdev); ethqos->phy_wol_supported = 0; ethqos->phy_wol_wolopts = 0; /* Check if phydev is valid*/ /* Check and enable Wake-on-LAN functionality in PHY*/ if (ndev->phydev) { struct ethtool_wolinfo wol = {.cmd = ETHTOOL_GWOL}; wol.supported = 0; wol.wolopts = 0; ETHQOSINFO("phydev addr: 0x%pK\n", ndev->phydev); phy_ethtool_get_wol(ndev->phydev, &wol); ethqos->phy_wol_supported = wol.supported; ETHQOSINFO("Get WoL[0x%x] in %s\n", wol.supported, ndev->phydev->drv->name); /* Try to enable supported Wake-on-LAN features in PHY*/ if (wol.supported) { device_set_wakeup_capable(ðqos->pdev->dev, 1); wol.cmd = ETHTOOL_SWOL; wol.wolopts = wol.supported; if (!phy_ethtool_set_wol(ndev->phydev, &wol)) { ethqos->phy_wol_wolopts = wol.wolopts; enable_irq_wake(ethqos->phy_intr); device_set_wakeup_enable(ðqos->pdev->dev, 1); ETHQOSINFO("Enabled WoL[0x%x] in %s\n", wol.wolopts, ndev->phydev->drv->name); } else { ETHQOSINFO("Disabled WoL[0x%x] in %s\n", wol.wolopts, ndev->phydev->drv->name); } } else { ETHQOSINFO("WoL Not Supported\n"); } } } static int qcom_ethqos_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; Loading @@ -888,6 +1018,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_mem; } ethqos->pdev = pdev; ethqos_init_reqgulators(ethqos); Loading Loading @@ -920,8 +1051,8 @@ static int qcom_ethqos_probe(struct platform_device *pdev) if (ret) goto err_mem; ethqos->speed = SPEED_1000; ethqos_update_rgmii_clk(ethqos, SPEED_1000); ethqos->speed = SPEED_10; ethqos_update_rgmii_clk_and_bus_cfg(ethqos, SPEED_10); ethqos_set_func_clk_en(ethqos); plat_dat->bsp_priv = ethqos; Loading Loading @@ -1010,20 +1141,86 @@ static int qcom_ethqos_remove(struct platform_device *pdev) if (phy_intr_en) free_irq(ethqos->phy_intr, ethqos); if (phy_intr_en) cancel_work_sync(ðqos->emac_phy_work); emac_emb_smmu_exit(); ethqos_disable_regulators(ethqos); return ret; } static int qcom_ethqos_suspend(struct device *dev) { struct qcom_ethqos *ethqos; struct net_device *ndev = NULL; int ret; if (of_device_is_compatible(dev->of_node, "qcom,emac-smmu-embedded")) { ETHQOSDBG("smmu return\n"); return 0; } ethqos = get_stmmac_bsp_priv(dev); if (!ethqos) return -ENODEV; ndev = dev_get_drvdata(dev); if (!ndev || !netif_running(ndev)) return -EINVAL; ret = stmmac_suspend(dev); qcom_ethqos_phy_suspend_clks(ethqos); ETHQOSDBG(" ret = %d\n", ret); return ret; } static int qcom_ethqos_resume(struct device *dev) { struct net_device *ndev = NULL; struct qcom_ethqos *ethqos; int ret; ETHQOSDBG("Resume Enter\n"); if (of_device_is_compatible(dev->of_node, "qcom,emac-smmu-embedded")) return 0; ethqos = get_stmmac_bsp_priv(dev); if (!ethqos) return -ENODEV; ndev = dev_get_drvdata(dev); if (!ndev || !netif_running(ndev)) { ETHQOSERR(" Resume not possible\n"); return -EINVAL; } qcom_ethqos_phy_resume_clks(ethqos); ret = stmmac_resume(dev); ETHQOSDBG("<--Resume Exit\n"); return ret; } MODULE_DEVICE_TABLE(of, qcom_ethqos_match); static const struct dev_pm_ops qcom_ethqos_pm_ops = { .suspend = qcom_ethqos_suspend, .resume = qcom_ethqos_resume, }; static struct platform_driver qcom_ethqos_driver = { .probe = qcom_ethqos_probe, .remove = qcom_ethqos_remove, .driver = { .name = DRV_NAME, .pm = &stmmac_pltfr_pm_ops, .pm = &qcom_ethqos_pm_ops, .of_match_table = of_match_ptr(qcom_ethqos_match), }, }; Loading
drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h +31 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ #ifndef _DWMAC_QCOM_ETHQOS_H #define _DWMAC_QCOM_ETHQOS_H //#include <linux/msm-bus.h> #define DRV_NAME "qcom-ethqos" #define ETHQOSDBG(fmt, args...) \ pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args) Loading Loading @@ -50,6 +52,11 @@ #define AVB_CLASS_A_CHANNEL_NUM 2 #define AVB_CLASS_B_CHANNEL_NUM 3 #define VOTE_IDX_0MBPS 0 #define VOTE_IDX_10MBPS 1 #define VOTE_IDX_100MBPS 2 #define VOTE_IDX_1000MBPS 3 static inline u32 PPSCMDX(u32 x, u32 val) { return (GENMASK(PPS_MINIDX(x) + 3, PPS_MINIDX(x)) & Loading @@ -67,6 +74,12 @@ static inline u32 PPSX_MASK(u32 x) return GENMASK(PPS_MAXIDX(x), PPS_MINIDX(x)); } enum IO_MACRO_PHY_MODE { RGMII_MODE, RMII_MODE, MII_MODE }; struct ethqos_emac_por { unsigned int offset; unsigned int value; Loading Loading @@ -94,9 +107,12 @@ struct qcom_ethqos { struct platform_device *pdev; void __iomem *rgmii_base; struct msm_bus_scale_pdata *bus_scale_vec; u32 bus_hdl; unsigned int rgmii_clk_rate; struct clk *rgmii_clk; unsigned int speed; unsigned int vote_idx; int gpio_phy_intr_redirect; u32 phy_intr; Loading Loading @@ -129,6 +145,17 @@ struct qcom_ethqos { unsigned long avb_class_a_intr_cnt; unsigned long avb_class_b_intr_cnt; /* saving state for Wake-on-LAN */ int wolopts; /* state of enabled wol options in PHY*/ u32 phy_wol_wolopts; /* state of supported wol options in PHY*/ u32 phy_wol_supported; /* Boolean to check if clock is suspended*/ int clks_suspended; /* Structure which holds done and wait members */ struct completion clk_enable_done; }; struct pps_cfg { Loading Loading @@ -164,6 +191,9 @@ int create_pps_interrupt_device_node(dev_t *pps_dev_t, struct cdev **pps_cdev, struct class **pps_class, char *pps_dev_node_name); bool qcom_ethqos_is_phy_link_up(struct qcom_ethqos *ethqos); void *qcom_ethqos_get_priv(struct qcom_ethqos *ethqos); int ppsout_config(struct stmmac_priv *priv, struct ifr_data_struct *req); u16 dwmac_qcom_select_queue(struct net_device *dev, Loading Loading @@ -221,4 +251,5 @@ struct dwmac_qcom_avb_algorithm { void dwmac_qcom_program_avb_algorithm(struct stmmac_priv *priv, struct ifr_data_struct *req); unsigned int dwmac_qcom_get_plat_tx_coal_frames(struct sk_buff *skb); void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat); #endif
drivers/net/ethernet/stmicro/stmmac/stmmac.h +2 −0 Original line number Diff line number Diff line Loading @@ -264,6 +264,8 @@ int ethqos_handle_prv_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); int ethqos_init_pps(struct stmmac_priv *priv); extern bool phy_intr_en; void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat_dat); int stmmac_mdio_unregister(struct net_device *ndev); int stmmac_mdio_register(struct net_device *ndev); int stmmac_mdio_reset(struct mii_bus *mii); Loading
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +1 −16 Original line number Diff line number Diff line Loading @@ -1054,6 +1054,7 @@ static int stmmac_init_phy(struct net_device *dev) !priv->phydev->drv->config_intr(priv->phydev)) { pr_err(" qcom-ethqos: %s config_phy_intr successful aftre connect\n", __func__); qcom_ethqos_request_phy_wol(priv->plat); } } else { pr_info("stmmac phy polling mode\n"); Loading Loading @@ -4042,22 +4043,6 @@ static int stmmac_setup_tc(struct net_device *ndev, enum tc_setup_type type, } } static u16 stmmac_select_queue(struct net_device *dev, struct sk_buff *skb, struct net_device *sb_dev) { if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { /* * There is no way to determine the number of TSO * capable Queues. Let's use always the Queue 0 * because if TSO is supported then at least this * one will be capable. */ return 0; } return netdev_pick_tx(dev, skb, NULL) % dev->real_num_tx_queues; } static int stmmac_set_mac_address(struct net_device *ndev, void *addr) { struct stmmac_priv *priv = netdev_priv(ndev); Loading
drivers/net/phy/micrel.c +139 −3 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <linux/of.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> /* Operation Mode Strap Override */ #define MII_KSZPHY_OMSO 0x16 Loading Loading @@ -72,6 +74,21 @@ #define PS_TO_REG 200 /*Register 2.10. 15:14 PME Output Select*/ #define MII_KSZPHY_OMSO_PME_N2 BIT(10) /*Register 2.10. BITS 6, 1 and 0 to detect the type of WOL */ #define MII_KSZPHY_WOL_MAGIC_PKT BIT(6) #define MII_KSZPHY_WOL_LINK_DOWN BIT(1) #define MII_KSZPHY_WOL_LINK_UP BIT(0) /* Register 2.10.15:14 PME Output Select */ #define MII_KSZPHY_WOL_CTRL_PME_N2 BIT(15) #define MII_KSZPHY_WOL_CTRL_INT_N BIT(14) /* MMD Address 2h, Register 2h Operation Mode Strap Override*/ #define MII_KSZPHY_OMSO_REG 0x2 /* MMD Address 2h, Register 10h Wake-On-LAN Control */ #define MII_KSZPHY_WOL_CTRL_REG 0x10 struct kszphy_hw_stat { const char *string; u8 reg; Loading Loading @@ -498,6 +515,32 @@ static int ksz9021_config_init(struct phy_device *phydev) #define MII_KSZ9031RN_EDPD 0x23 #define MII_KSZ9031RN_EDPD_ENABLE BIT(0) static int ksz9031_ack_interrupt(struct phy_device *phydev) { /* bit[7..0] int status, which is a read and clear register. */ int rc; u32 reg_value; rc = phy_read(phydev, MII_KSZPHY_INTCS); reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG); if (reg_value & MII_KSZPHY_OMSO_PME_N2) { /* PME output is cleared by disabling the PME trigger src */ reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value &= ~MII_KSZPHY_WOL_MAGIC_PKT; reg_value &= ~MII_KSZPHY_WOL_LINK_UP; reg_value &= ~MII_KSZPHY_WOL_LINK_DOWN; phy_write_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value |= MII_KSZPHY_WOL_MAGIC_PKT; reg_value |= MII_KSZPHY_WOL_LINK_UP; reg_value |= MII_KSZPHY_WOL_LINK_DOWN; phy_write_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); } return (rc < 0) ? rc : 0; } static int ksz9031_of_load_skew_values(struct phy_device *phydev, const struct device_node *of_node, u16 reg, size_t field_sz, Loading Loading @@ -984,6 +1027,97 @@ static int kszphy_probe(struct phy_device *phydev) return 0; } static void ksz9031_set_wol_settings(struct phy_device *phydev) { u32 reg_value; /* Enable both PHY and PME_N2 interrupts */ reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value |= MII_KSZPHY_WOL_CTRL_PME_N2; reg_value &= ~MII_KSZPHY_WOL_CTRL_INT_N; reg_value |= MII_KSZPHY_WOL_MAGIC_PKT; reg_value |= MII_KSZPHY_WOL_LINK_UP; reg_value |= MII_KSZPHY_WOL_LINK_DOWN; phy_write_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); } static int ksz9031_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { struct net_device *ndev = phydev->attached_dev; const u8 *mac; int ret = 0; u32 reg_value; if (!ndev) return -ENODEV; if (wol->wolopts & WAKE_MAGIC) { mac = (const u8 *)ndev->dev_addr; if (!is_valid_ether_addr(mac)) return -EINVAL; phy_write_mmd(phydev, 0x2, 0x11, mac[5] | (mac[4] << 8)); phy_write_mmd(phydev, 0x2, 0x12, mac[3] | (mac[2] << 8)); phy_write_mmd(phydev, 0x2, 0x13, mac[1] | (mac[0] << 8)); /* Enable WOL interrupt for magic pkt, link up and down */ ksz9031_set_wol_settings(phydev); /* Enable PME_N2 output */ reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG); reg_value |= MII_KSZPHY_OMSO_PME_N2; phy_write_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG, reg_value); } return ret; } static void ksz9031_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { u32 reg_value; wol->supported = WAKE_MAGIC; wol->wolopts = 0; reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG); if (reg_value & MII_KSZPHY_OMSO_PME_N2) wol->wolopts |= WAKE_MAGIC; } static int ksz9031_suspend(struct phy_device *phydev) { int value; int wol_enabled; u32 reg_value; reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG); wol_enabled = reg_value & MII_KSZPHY_OMSO_PME_N2; value = phy_read(phydev, MII_BMCR); if (wol_enabled) value |= BMCR_ISOLATE; else value |= BMCR_PDOWN; phy_write(phydev, MII_BMCR, value); return 0; } static int ksz9031_resume(struct phy_device *phydev) { int value; value = phy_read(phydev, MII_BMCR); value &= ~(BMCR_PDOWN | BMCR_ISOLATE); phy_write(phydev, MII_BMCR, value); return 0; } static struct phy_driver ksphy_driver[] = { { .phy_id = PHY_ID_KS8737, Loading Loading @@ -1139,13 +1273,15 @@ static struct phy_driver ksphy_driver[] = { .config_init = ksz9031_config_init, .soft_reset = genphy_soft_reset, .read_status = ksz9031_read_status, .ack_interrupt = kszphy_ack_interrupt, .ack_interrupt = ksz9031_ack_interrupt, .config_intr = kszphy_config_intr, .get_sset_count = kszphy_get_sset_count, .get_strings = kszphy_get_strings, .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = kszphy_resume, .set_wol = ksz9031_set_wol, .get_wol = ksz9031_get_wol, .suspend = ksz9031_suspend, .resume = ksz9031_resume, }, { .phy_id = PHY_ID_KSZ9131, .phy_id_mask = MICREL_PHY_ID_MASK, Loading