Loading arch/arm/configs/vendor/sa515m-perf_defconfig +1 −0 Original line number Diff line number Diff line Loading @@ -398,6 +398,7 @@ CONFIG_SSR_SYSMON_NOTIF_TIMEOUT=20000 CONFIG_SSR_SUBSYS_NOTIF_TIMEOUT=20000 CONFIG_PANIC_ON_SSR_NOTIF_TIMEOUT=y CONFIG_MSM_BOOT_STATS=y CONFIG_MSM_BOOT_TIME_MARKER=y CONFIG_QCOM_DCC_V2=y CONFIG_QCOM_SECURE_BUFFER=y CONFIG_QCOM_EUD=y Loading arch/arm/configs/vendor/sa515m_defconfig +1 −0 Original line number Diff line number Diff line Loading @@ -404,6 +404,7 @@ CONFIG_SSR_SYSMON_NOTIF_TIMEOUT=20000 CONFIG_SSR_SUBSYS_NOTIF_TIMEOUT=20000 CONFIG_PANIC_ON_SSR_NOTIF_TIMEOUT=y CONFIG_MSM_BOOT_STATS=y CONFIG_MSM_BOOT_TIME_MARKER=y CONFIG_QCOM_DCC_V2=y CONFIG_QCOM_SECURE_BUFFER=y CONFIG_QCOM_EUD=y Loading arch/arm64/boot/dts/qcom/sdxprairie.dtsi +9 −1 Original line number Diff line number Diff line Loading @@ -1481,7 +1481,15 @@ <&tlmm 90 2>, <&pdc 0 290 1>, <&pdc 0 291 1>; interrupt-names = "macirq", "eth_lpi", "phy-intr", "ptp_pps_irq_0", "ptp_pps_irq_1"; qcom,msm-bus,name = "emac"; qcom,msm-bus,num-cases = <4>; qcom,msm-bus,num-paths = <2>; qcom,msm-bus,vectors-KBps = <98 512 0 0>, <1 781 0 0>, /* No vote */ <98 512 2500 0>, <1 781 0 40000>, /* 10Mbps vote */ <98 512 25000 0>, <1 781 0 40000>, /* 100Mbps vote */ <98 512 250000 0>, <1 781 0 40000>; /* 1000Mbps vote */ qcom,bus-vector-names = "0", "10", "100", "1000"; snps,tso; rx-fifo-depth = <16384>; tx-fifo-depth = <16384>; Loading drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +284 −6 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ struct qcom_ethqos *pethqos; struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0}; void *ipc_emac_log_ctxt; static struct qmp_pkt pkt; static char qmp_buf[MAX_QMP_MSG_SIZE + 1] = {0}; static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset) { Loading Loading @@ -82,8 +84,11 @@ 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) { int ret; switch (speed) { case SPEED_1000: ethqos->rgmii_clk_rate = RGMII_1000_NOM_CLK_FREQ; Loading @@ -98,9 +103,99 @@ 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; } if (ethqos->bus_hdl) { ret = msm_bus_scale_client_update_request(ethqos->bus_hdl, ethqos->vote_idx); WARN_ON(ret); } clk_set_rate(ethqos->rgmii_clk, ethqos->rgmii_clk_rate); } static int qcom_ethqos_qmp_mailbox_init(struct qcom_ethqos *ethqos) { ethqos->qmp_mbox_client = devm_kzalloc( ðqos->pdev->dev, sizeof(*ethqos->qmp_mbox_client), GFP_KERNEL); if (IS_ERR(ethqos->qmp_mbox_client)) { ETHQOSERR("qmp alloc client failed\n"); return -EINVAL; } ethqos->qmp_mbox_client->dev = ðqos->pdev->dev; ethqos->qmp_mbox_client->tx_block = true; ethqos->qmp_mbox_client->tx_tout = 1000; ethqos->qmp_mbox_client->knows_txdone = false; ethqos->qmp_mbox_chan = mbox_request_channel(ethqos->qmp_mbox_client, 0); if (IS_ERR(ethqos->qmp_mbox_chan)) { ETHQOSERR("qmp request channel failed\n"); return -EINVAL; } return 0; } static int qcom_ethqos_qmp_mailbox_send_message(struct qcom_ethqos *ethqos) { int ret = 0; memset(&qmp_buf[0], 0, MAX_QMP_MSG_SIZE + 1); snprintf(qmp_buf, MAX_QMP_MSG_SIZE, "{class:ctile, pc:0}"); pkt.size = ((size_t)strlen(qmp_buf) + 0x3) & ~0x3; pkt.data = qmp_buf; ret = mbox_send_message(ethqos->qmp_mbox_chan, (void *)&pkt); ETHQOSDBG("qmp mbox_send_message ret = %d\n", ret); if (ret < 0) { ETHQOSERR("Disabling c-tile power collapse failed\n"); return ret; } ETHQOSDBG("Disabling c-tile power collapse succeded"); return 0; } /** * DWC_ETH_QOS_qmp_mailbox_work - Scheduled from probe * @work: work_struct */ static void qcom_ethqos_qmp_mailbox_work(struct work_struct *work) { struct qcom_ethqos *ethqos = container_of(work, struct qcom_ethqos, qmp_mailbox_work); ETHQOSDBG("Enter\n"); /* Send QMP message to disable c-tile power collapse */ qcom_ethqos_qmp_mailbox_send_message(ethqos); ETHQOSDBG("Exit\n"); } static void ethqos_set_func_clk_en(struct qcom_ethqos *ethqos) { rgmii_updatel(ethqos, RGMII_CONFIG_FUNC_CLK_EN, Loading Loading @@ -411,7 +506,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 @@ -519,6 +614,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 @@ -526,6 +624,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 @@ -536,6 +636,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 @@ -936,6 +1038,108 @@ static int ethqos_update_rgmii_tx_drv_strength(struct qcom_ethqos *ethqos) return ret; } 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"); } static bool qcom_ethqos_is_phy_link_up(struct qcom_ethqos *ethqos, struct net_device *ndev) { if (!ethqos) { ETHQOSINFO("ethqos addr is NULL"); return 0; } if (!ndev) { ETHQOSINFO("dev addr is NULL"); return 0; } /* PHY driver initializes phydev->link=1. * So, phydev->link is 1 even on booup with no PHY connected. * phydev->link is valid only after adjust_link is called once. * Use (pdata->oldlink != -1) to indicate phy link is not up */ return ethqos->always_on_phy ? 1 : ((ethqos->oldlink != -1) && ndev->phydev && ndev->phydev->link); return 0; } static void qcom_ethqos_phy_resume_clks(struct qcom_ethqos *ethqos, struct net_device *ndev) { ETHQOSINFO("Enter\n"); if (qcom_ethqos_is_phy_link_up(ethqos, ndev)) 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); } } } } static int qcom_ethqos_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; Loading Loading @@ -963,8 +1167,9 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_mem; } ethqos->pdev = pdev; ethqos->pdev = pdev; ethqos->oldlink = -1; ethqos_init_reqgulators(ethqos); ethqos_init_gpio(ethqos); Loading Loading @@ -995,9 +1200,11 @@ 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); if (ethqos->emac_ver == EMAC_HW_v2_0_0) ethqos->disable_ctile_pc = 1; plat_dat->bsp_priv = ethqos; plat_dat->fix_mac_speed = ethqos_fix_mac_speed; Loading Loading @@ -1047,6 +1254,11 @@ static int qcom_ethqos_probe(struct platform_device *pdev) AVB_CLASS_B_POLL_DEV_NODE); } if (ethqos->disable_ctile_pc && !qcom_ethqos_qmp_mailbox_init(ethqos)) { INIT_WORK(ðqos->qmp_mailbox_work, qcom_ethqos_qmp_mailbox_work); queue_work(system_wq, ðqos->qmp_mailbox_work); } pethqos = ethqos; ethqos_create_debugfs(ethqos); return ret; Loading Loading @@ -1074,20 +1286,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, ndev); 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 = "qcom-ethqos", .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 +39 −3 Original line number Diff line number Diff line /* Copyright (c) 2019, The Linux Foundation. All rights reserved. /* Copyright (c) 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -13,10 +13,15 @@ #define _DWMAC_QCOM_ETHQOS_H #include <linux/ipc_logging.h> #include <linux/msm-bus.h> #include <linux/mailbox_client.h> #include <linux/mailbox/qmp.h> #include <linux/mailbox_controller.h> extern void *ipc_emac_log_ctxt; #define IPCLOG_STATE_PAGES 50 #define MAX_QMP_MSG_SIZE 96 #define __FILENAME__ (strrchr(__FILE__, '/') ? \ strrchr(__FILE__, '/') + 1 : __FILE__) Loading Loading @@ -153,12 +158,16 @@ do {\ #define MICREL_PHY_ID PHY_ID_KSZ9031 #define DWC_ETH_QOS_MICREL_PHY_INTCS 0x1b #define DWC_ETH_QOS_MICREL_PHY_CTL 0x1f #define DWC_ETH_QOS_MICREL_INTR_LEVEL 0x4000 #define DWC_ETH_QOS_BASIC_STATUS 0x0001 #define LINK_STATE_MASK 0x4 #define AUTONEG_STATE_MASK 0x20 #define MICREL_LINK_UP_INTR_STATUS BIT(0) #define VOTE_IDX_0MBPS 0 #define VOTE_IDX_10MBPS 1 #define VOTE_IDX_100MBPS 2 #define VOTE_IDX_1000MBPS 3 #define TLMM_BASE_ADDRESS (tlmm_central_base_addr) #define TLMM_RGMII_HDRV_PULL_CTL1_ADDRESS_OFFSET\ Loading Loading @@ -235,7 +244,6 @@ do {\ #define TLMM_RGMII_RX_HV_MODE_CTL_RGRD(data)\ ((data) = ioread32((void __iomem *)TLMM_RGMII_RX_HV_MODE_CTL_ADDRESS)) static inline u32 PPSCMDX(u32 x, u32 val) { return (GENMASK(PPS_MINIDX(x) + 3, PPS_MINIDX(x)) & Loading @@ -253,6 +261,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 @@ -280,9 +294,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 @@ -317,7 +334,24 @@ struct qcom_ethqos { unsigned long avb_class_b_intr_cnt; struct dentry *debugfs_dir; int oldlink; /* 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; int always_on_phy; /* QMP message for disabling ctile power collapse while XO shutdown */ struct mbox_chan *qmp_mbox_chan; struct mbox_client *qmp_mbox_client; struct work_struct qmp_mailbox_work; int disable_ctile_pc; }; struct pps_cfg { Loading Loading @@ -353,4 +387,6 @@ int create_pps_interrupt_device_node(dev_t *pps_dev_t, struct cdev **pps_cdev, struct class **pps_class, char *pps_dev_node_name); void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat); #endif Loading
arch/arm/configs/vendor/sa515m-perf_defconfig +1 −0 Original line number Diff line number Diff line Loading @@ -398,6 +398,7 @@ CONFIG_SSR_SYSMON_NOTIF_TIMEOUT=20000 CONFIG_SSR_SUBSYS_NOTIF_TIMEOUT=20000 CONFIG_PANIC_ON_SSR_NOTIF_TIMEOUT=y CONFIG_MSM_BOOT_STATS=y CONFIG_MSM_BOOT_TIME_MARKER=y CONFIG_QCOM_DCC_V2=y CONFIG_QCOM_SECURE_BUFFER=y CONFIG_QCOM_EUD=y Loading
arch/arm/configs/vendor/sa515m_defconfig +1 −0 Original line number Diff line number Diff line Loading @@ -404,6 +404,7 @@ CONFIG_SSR_SYSMON_NOTIF_TIMEOUT=20000 CONFIG_SSR_SUBSYS_NOTIF_TIMEOUT=20000 CONFIG_PANIC_ON_SSR_NOTIF_TIMEOUT=y CONFIG_MSM_BOOT_STATS=y CONFIG_MSM_BOOT_TIME_MARKER=y CONFIG_QCOM_DCC_V2=y CONFIG_QCOM_SECURE_BUFFER=y CONFIG_QCOM_EUD=y Loading
arch/arm64/boot/dts/qcom/sdxprairie.dtsi +9 −1 Original line number Diff line number Diff line Loading @@ -1481,7 +1481,15 @@ <&tlmm 90 2>, <&pdc 0 290 1>, <&pdc 0 291 1>; interrupt-names = "macirq", "eth_lpi", "phy-intr", "ptp_pps_irq_0", "ptp_pps_irq_1"; qcom,msm-bus,name = "emac"; qcom,msm-bus,num-cases = <4>; qcom,msm-bus,num-paths = <2>; qcom,msm-bus,vectors-KBps = <98 512 0 0>, <1 781 0 0>, /* No vote */ <98 512 2500 0>, <1 781 0 40000>, /* 10Mbps vote */ <98 512 25000 0>, <1 781 0 40000>, /* 100Mbps vote */ <98 512 250000 0>, <1 781 0 40000>; /* 1000Mbps vote */ qcom,bus-vector-names = "0", "10", "100", "1000"; snps,tso; rx-fifo-depth = <16384>; tx-fifo-depth = <16384>; Loading
drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +284 −6 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ struct qcom_ethqos *pethqos; struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0}; void *ipc_emac_log_ctxt; static struct qmp_pkt pkt; static char qmp_buf[MAX_QMP_MSG_SIZE + 1] = {0}; static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset) { Loading Loading @@ -82,8 +84,11 @@ 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) { int ret; switch (speed) { case SPEED_1000: ethqos->rgmii_clk_rate = RGMII_1000_NOM_CLK_FREQ; Loading @@ -98,9 +103,99 @@ 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; } if (ethqos->bus_hdl) { ret = msm_bus_scale_client_update_request(ethqos->bus_hdl, ethqos->vote_idx); WARN_ON(ret); } clk_set_rate(ethqos->rgmii_clk, ethqos->rgmii_clk_rate); } static int qcom_ethqos_qmp_mailbox_init(struct qcom_ethqos *ethqos) { ethqos->qmp_mbox_client = devm_kzalloc( ðqos->pdev->dev, sizeof(*ethqos->qmp_mbox_client), GFP_KERNEL); if (IS_ERR(ethqos->qmp_mbox_client)) { ETHQOSERR("qmp alloc client failed\n"); return -EINVAL; } ethqos->qmp_mbox_client->dev = ðqos->pdev->dev; ethqos->qmp_mbox_client->tx_block = true; ethqos->qmp_mbox_client->tx_tout = 1000; ethqos->qmp_mbox_client->knows_txdone = false; ethqos->qmp_mbox_chan = mbox_request_channel(ethqos->qmp_mbox_client, 0); if (IS_ERR(ethqos->qmp_mbox_chan)) { ETHQOSERR("qmp request channel failed\n"); return -EINVAL; } return 0; } static int qcom_ethqos_qmp_mailbox_send_message(struct qcom_ethqos *ethqos) { int ret = 0; memset(&qmp_buf[0], 0, MAX_QMP_MSG_SIZE + 1); snprintf(qmp_buf, MAX_QMP_MSG_SIZE, "{class:ctile, pc:0}"); pkt.size = ((size_t)strlen(qmp_buf) + 0x3) & ~0x3; pkt.data = qmp_buf; ret = mbox_send_message(ethqos->qmp_mbox_chan, (void *)&pkt); ETHQOSDBG("qmp mbox_send_message ret = %d\n", ret); if (ret < 0) { ETHQOSERR("Disabling c-tile power collapse failed\n"); return ret; } ETHQOSDBG("Disabling c-tile power collapse succeded"); return 0; } /** * DWC_ETH_QOS_qmp_mailbox_work - Scheduled from probe * @work: work_struct */ static void qcom_ethqos_qmp_mailbox_work(struct work_struct *work) { struct qcom_ethqos *ethqos = container_of(work, struct qcom_ethqos, qmp_mailbox_work); ETHQOSDBG("Enter\n"); /* Send QMP message to disable c-tile power collapse */ qcom_ethqos_qmp_mailbox_send_message(ethqos); ETHQOSDBG("Exit\n"); } static void ethqos_set_func_clk_en(struct qcom_ethqos *ethqos) { rgmii_updatel(ethqos, RGMII_CONFIG_FUNC_CLK_EN, Loading Loading @@ -411,7 +506,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 @@ -519,6 +614,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 @@ -526,6 +624,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 @@ -536,6 +636,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 @@ -936,6 +1038,108 @@ static int ethqos_update_rgmii_tx_drv_strength(struct qcom_ethqos *ethqos) return ret; } 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"); } static bool qcom_ethqos_is_phy_link_up(struct qcom_ethqos *ethqos, struct net_device *ndev) { if (!ethqos) { ETHQOSINFO("ethqos addr is NULL"); return 0; } if (!ndev) { ETHQOSINFO("dev addr is NULL"); return 0; } /* PHY driver initializes phydev->link=1. * So, phydev->link is 1 even on booup with no PHY connected. * phydev->link is valid only after adjust_link is called once. * Use (pdata->oldlink != -1) to indicate phy link is not up */ return ethqos->always_on_phy ? 1 : ((ethqos->oldlink != -1) && ndev->phydev && ndev->phydev->link); return 0; } static void qcom_ethqos_phy_resume_clks(struct qcom_ethqos *ethqos, struct net_device *ndev) { ETHQOSINFO("Enter\n"); if (qcom_ethqos_is_phy_link_up(ethqos, ndev)) 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); } } } } static int qcom_ethqos_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; Loading Loading @@ -963,8 +1167,9 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_mem; } ethqos->pdev = pdev; ethqos->pdev = pdev; ethqos->oldlink = -1; ethqos_init_reqgulators(ethqos); ethqos_init_gpio(ethqos); Loading Loading @@ -995,9 +1200,11 @@ 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); if (ethqos->emac_ver == EMAC_HW_v2_0_0) ethqos->disable_ctile_pc = 1; plat_dat->bsp_priv = ethqos; plat_dat->fix_mac_speed = ethqos_fix_mac_speed; Loading Loading @@ -1047,6 +1254,11 @@ static int qcom_ethqos_probe(struct platform_device *pdev) AVB_CLASS_B_POLL_DEV_NODE); } if (ethqos->disable_ctile_pc && !qcom_ethqos_qmp_mailbox_init(ethqos)) { INIT_WORK(ðqos->qmp_mailbox_work, qcom_ethqos_qmp_mailbox_work); queue_work(system_wq, ðqos->qmp_mailbox_work); } pethqos = ethqos; ethqos_create_debugfs(ethqos); return ret; Loading Loading @@ -1074,20 +1286,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, ndev); 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 = "qcom-ethqos", .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 +39 −3 Original line number Diff line number Diff line /* Copyright (c) 2019, The Linux Foundation. All rights reserved. /* Copyright (c) 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -13,10 +13,15 @@ #define _DWMAC_QCOM_ETHQOS_H #include <linux/ipc_logging.h> #include <linux/msm-bus.h> #include <linux/mailbox_client.h> #include <linux/mailbox/qmp.h> #include <linux/mailbox_controller.h> extern void *ipc_emac_log_ctxt; #define IPCLOG_STATE_PAGES 50 #define MAX_QMP_MSG_SIZE 96 #define __FILENAME__ (strrchr(__FILE__, '/') ? \ strrchr(__FILE__, '/') + 1 : __FILE__) Loading Loading @@ -153,12 +158,16 @@ do {\ #define MICREL_PHY_ID PHY_ID_KSZ9031 #define DWC_ETH_QOS_MICREL_PHY_INTCS 0x1b #define DWC_ETH_QOS_MICREL_PHY_CTL 0x1f #define DWC_ETH_QOS_MICREL_INTR_LEVEL 0x4000 #define DWC_ETH_QOS_BASIC_STATUS 0x0001 #define LINK_STATE_MASK 0x4 #define AUTONEG_STATE_MASK 0x20 #define MICREL_LINK_UP_INTR_STATUS BIT(0) #define VOTE_IDX_0MBPS 0 #define VOTE_IDX_10MBPS 1 #define VOTE_IDX_100MBPS 2 #define VOTE_IDX_1000MBPS 3 #define TLMM_BASE_ADDRESS (tlmm_central_base_addr) #define TLMM_RGMII_HDRV_PULL_CTL1_ADDRESS_OFFSET\ Loading Loading @@ -235,7 +244,6 @@ do {\ #define TLMM_RGMII_RX_HV_MODE_CTL_RGRD(data)\ ((data) = ioread32((void __iomem *)TLMM_RGMII_RX_HV_MODE_CTL_ADDRESS)) static inline u32 PPSCMDX(u32 x, u32 val) { return (GENMASK(PPS_MINIDX(x) + 3, PPS_MINIDX(x)) & Loading @@ -253,6 +261,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 @@ -280,9 +294,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 @@ -317,7 +334,24 @@ struct qcom_ethqos { unsigned long avb_class_b_intr_cnt; struct dentry *debugfs_dir; int oldlink; /* 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; int always_on_phy; /* QMP message for disabling ctile power collapse while XO shutdown */ struct mbox_chan *qmp_mbox_chan; struct mbox_client *qmp_mbox_client; struct work_struct qmp_mailbox_work; int disable_ctile_pc; }; struct pps_cfg { Loading Loading @@ -353,4 +387,6 @@ int create_pps_interrupt_device_node(dev_t *pps_dev_t, struct cdev **pps_cdev, struct class **pps_class, char *pps_dev_node_name); void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat); #endif