diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2022-12-06 14:38:51 -0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2022-12-06 14:38:51 -0800 |
commit | 86f6506147e2ed1a725bb97484a72e5888e91736 (patch) | |
tree | 0f33da399c2e71c2eb1e51df354031cdfec97d81 | |
parent | 51ab7643922fd1069390943fe803760506e6bd61 (diff) | |
parent | 88a24e4ecdf5774ef19b95ec60d7faeaec6d95a8 (diff) |
Merge "net: stmmac: Add support for PHY interrupts" into wip-er12
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 136 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c | 64 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 12 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 33 | ||||
-rw-r--r-- | include/linux/stmmac.h | 5 |
7 files changed, 248 insertions, 9 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index fee848a25dc4a..b8548bf5676aa 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -7,6 +7,12 @@ #include <linux/platform_device.h> #include <linux/phy.h> #include <linux/pm_runtime.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/mii.h> +#include <linux/of_mdio.h> +#include <linux/slab.h> +#include <linux/poll.h> #include <linux/dma-iommu.h> #include <linux/iommu.h> @@ -72,6 +78,25 @@ #define EMAC_I0_EMAC_CORE_HW_VERSION_RGOFFADDR 0x00000070 #define EMAC_HW_v3_0_0_RG 0x30000000 +#define MII_BUSY 0x00000001 +#define MII_WRITE 0x00000002 + +/* GMAC4 defines */ +#define MII_GMAC4_GOC_SHIFT 2 +#define MII_GMAC4_WRITE BIT(MII_GMAC4_GOC_SHIFT) +#define MII_GMAC4_READ (3 << MII_GMAC4_GOC_SHIFT) + +#define MII_BUSY 0x00000001 +#define MII_WRITE 0x00000002 + +#define DWC_ETH_QOS_PHY_INTR_STATUS 0x0013 + +#define LINK_UP 1 +#define LINK_DOWN 0 + +#define LINK_DOWN_STATE 0x800 +#define LINK_UP_STATE 0x400 + struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0}; struct plat_stmmacenet_data *plat_dat; @@ -510,6 +535,107 @@ static void ethqos_fix_mac_speed(void *priv, unsigned int speed) ethqos_configure(ethqos); } +static int ethqos_mdio_read(struct stmmac_priv *priv, int phyaddr, int phyreg) +{ + unsigned int mii_address = priv->hw->mii.addr; + unsigned int mii_data = priv->hw->mii.data; + u32 v; + int data; + u32 value = MII_BUSY; + + value |= (phyaddr << priv->hw->mii.addr_shift) + & priv->hw->mii.addr_mask; + value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask; + value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift) + & priv->hw->mii.clk_csr_mask; + if (priv->plat->has_gmac4) + value |= MII_GMAC4_READ; + + if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), + 100, 10000)) + return -EBUSY; + + writel_relaxed(value, priv->ioaddr + mii_address); + + if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), + 100, 10000)) + return -EBUSY; + + /* Read the data from the MII data register */ + data = (int)readl_relaxed(priv->ioaddr + mii_data); + + return data; +} + +static int ethqos_phy_intr_config(struct qcom_ethqos *ethqos) +{ + int ret = 0; + + ethqos->phy_intr = platform_get_irq_byname(ethqos->pdev, "phy-intr"); + + if (ethqos->phy_intr < 0) { + if (ethqos->phy_intr != -EPROBE_DEFER) + ETHQOSERR("PHY IRQ configuration information not found\n"); + ret = 1; + } + + return ret; +} + +static void ethqos_handle_phy_interrupt(struct qcom_ethqos *ethqos) +{ + int phy_intr_status = 0; + struct platform_device *pdev = ethqos->pdev; + + struct net_device *dev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(dev); + + phy_intr_status = ethqos_mdio_read(priv, priv->plat->phy_addr, + DWC_ETH_QOS_PHY_INTR_STATUS); + + if ((phy_intr_status & LINK_UP_STATE) || + (phy_intr_status & LINK_DOWN_STATE)) + phy_mac_interrupt(priv->phydev); +} + +static void ethqos_defer_phy_isr_work(struct work_struct *work) +{ + struct qcom_ethqos *ethqos = + container_of(work, struct qcom_ethqos, emac_phy_work); + + ethqos_handle_phy_interrupt(ethqos); +} + +static irqreturn_t ETHQOS_PHY_ISR(int irq, void *dev_data) +{ + struct qcom_ethqos *ethqos = (struct qcom_ethqos *)dev_data; + + queue_work(system_wq, ðqos->emac_phy_work); + return IRQ_HANDLED; +} + +static int ethqos_phy_intr_enable(void *priv_n) +{ + int ret = 0; + struct stmmac_priv *priv = priv_n; + struct qcom_ethqos *ethqos = priv->plat->bsp_priv; + + ret = ethqos_phy_intr_config(ethqos); + if (ret) + return ret; + + INIT_WORK(ðqos->emac_phy_work, ethqos_defer_phy_isr_work); + ret = request_irq(ethqos->phy_intr, ETHQOS_PHY_ISR, + IRQF_SHARED, "stmmac", ethqos); + if (ret) { + ETHQOSERR("Unable to register PHY IRQ %d\n", + ethqos->phy_intr); + return ret; + } + priv->plat->phy_intr_en_extn_stm = true; + return ret; +} + static const struct of_device_id qcom_ethqos_match[] = { { .compatible = "qcom,stmmac-ethqos-emac1", }, { .compatible = "qcom,emac-smmu-embedded", }, @@ -625,6 +751,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) plat_dat->has_gmac4 = 1; plat_dat->pmt = 1; plat_dat->tso_en = of_property_read_bool(np, "snps,tso"); + plat_dat->phy_intr_enable = ethqos_phy_intr_enable; /* Get rgmii interface speed for mac2c from device tree */ if (of_property_read_u32(np, "mac2mac-rgmii-speed", @@ -696,6 +823,7 @@ static int qcom_ethqos_remove(struct platform_device *pdev) { struct qcom_ethqos *ethqos; int ret; + struct stmmac_priv *priv; if (of_device_is_compatible(pdev->dev.of_node, "qcom,emac-smmu-embedded")) { @@ -707,9 +835,17 @@ static int qcom_ethqos_remove(struct platform_device *pdev) if (!ethqos) return -ENODEV; + priv = qcom_ethqos_get_priv(ethqos); + ret = stmmac_pltfr_remove(pdev); clk_disable_unprepare(ethqos->rgmii_clk); + if (priv->plat->phy_intr_en_extn_stm) { + cancel_work_sync(ðqos->emac_phy_work); + free_irq(ethqos->phy_intr, ethqos); + } + + ethqos_free_gpios(ethqos); emac_emb_smmu_exit(); ethqos_disable_regulators(ethqos); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h index e2755c49e3d76..b4d038f0b04e0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h @@ -40,6 +40,11 @@ struct qcom_ethqos { struct clk *rgmii_clk; unsigned int speed; + int gpio_phy_intr_redirect; + u32 phy_intr; + /* Work struct for handling phy interrupt */ + struct work_struct emac_phy_work; + struct ethqos_emac_por *por; unsigned int num_por; unsigned int emac_ver; @@ -52,5 +57,6 @@ struct qcom_ethqos { int ethqos_init_reqgulators(struct qcom_ethqos *ethqos); void ethqos_disable_regulators(struct qcom_ethqos *ethqos); int ethqos_init_gpio(struct qcom_ethqos *ethqos); +void ethqos_free_gpios(struct qcom_ethqos *ethqos); void *qcom_ethqos_get_priv(struct qcom_ethqos *ethqos); #endif diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c index 6fb5eefe568c8..f2b4512f7cab7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c @@ -16,6 +16,44 @@ #define EMAC_VREG_EMAC_PHY_NAME "vreg_emac_phy" #define EMAC_VREG_RGMII_IO_PADS_NAME "vreg_rgmii_io_pads" +static int setup_gpio_input_common + (struct device *dev, const char *name, int *gpio) +{ + int ret = 0; + + if (of_find_property(dev->of_node, name, NULL)) { + *gpio = ret = of_get_named_gpio(dev->of_node, name, 0); + if (ret >= 0) { + ret = gpio_request(*gpio, name); + if (ret) { + ETHQOSERR("Can't get GPIO %s, ret = %d\n", + name, *gpio); + *gpio = -1; + return ret; + } + + ret = gpio_direction_input(*gpio); + if (ret) { + ETHQOSERR("failed GPIO %s direction ret=%d\n", + name, ret); + return ret; + } + } else { + if (ret == -EPROBE_DEFER) + ETHQOSERR("get EMAC_GPIO probe defer\n"); + else + ETHQOSERR("can't get gpio %s ret %d\n", name, + ret); + return ret; + } + } else { + ETHQOSERR("can't find gpio %s\n", name); + ret = -EINVAL; + } + + return ret; +} + int ethqos_init_reqgulators(struct qcom_ethqos *ethqos) { int ret = 0; @@ -125,8 +163,7 @@ static int ethqos_init_pinctrl(struct device *dev) num_names = of_property_count_strings(dev->of_node, "pinctrl-names"); if (num_names < 0) { - dev_err(dev, "Cannot parse pinctrl-names: %d\n", - num_names); + ETHQOSERR("Cannot parse pinctrl-names: %d\n", num_names); devm_pinctrl_put(pinctrl); return num_names; } @@ -164,16 +201,39 @@ err_pinctrl: return ret; } +void ethqos_free_gpios(struct qcom_ethqos *ethqos) +{ + if (gpio_is_valid(ethqos->gpio_phy_intr_redirect)) + gpio_free(ethqos->gpio_phy_intr_redirect); + ethqos->gpio_phy_intr_redirect = -1; +} +EXPORT_SYMBOL(ethqos_free_gpios); + int ethqos_init_gpio(struct qcom_ethqos *ethqos) { int ret = 0; + ethqos->gpio_phy_intr_redirect = -1; + ret = ethqos_init_pinctrl(ðqos->pdev->dev); if (ret) { ETHQOSERR("ethqos_init_pinctrl failed"); return ret; } + ret = setup_gpio_input_common(ðqos->pdev->dev, + "qcom,phy-intr-redirect", + ðqos->gpio_phy_intr_redirect); + if (ret) { + ETHQOSERR("Failed to setup <%s> gpio\n", + "qcom,phy-intr-redirect"); + goto gpio_error; + } + + return ret; + +gpio_error: + ethqos_free_gpios(ethqos); return ret; } EXPORT_SYMBOL(ethqos_init_gpio); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 32ef02648022e..b7626aebe5975 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -197,6 +197,7 @@ struct stmmac_priv { struct mac_device_info *hw; int (*hwif_quirks)(struct stmmac_priv *priv); struct mutex lock; + struct phy_device *phydev; /* RX Queue */ struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES]; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 6bd62abbe65e6..301519fbab74e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -683,6 +683,12 @@ static void stmmac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct stmmac_priv *priv = netdev_priv(dev); + if (!priv->phydev) { + pr_err("%s: %s: PHY is not registered\n", + __func__, dev->name); + return; + } + if (!priv->plat->mac2mac_en && !priv->plat->pmt) return phylink_ethtool_get_wol(priv->phylink, wol); @@ -701,6 +707,12 @@ static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) struct stmmac_priv *priv = netdev_priv(dev); u32 support = WAKE_MAGIC | WAKE_UCAST; + if (!priv->phydev) { + pr_err("%s: %s: PHY is not registered\n", + __func__, dev->name); + return -ENODEV; + } + if (!device_can_wakeup(priv->device)) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 9413fee83b911..6e57513b94656 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -402,8 +402,12 @@ static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv) return; } - priv->plat->fix_mac_speed(priv->plat->bsp_priv, - SPEED_10); + if (priv->phydev->link) + priv->plat->fix_mac_speed(priv->plat->bsp_priv, + priv->speed); + else + priv->plat->fix_mac_speed(priv->plat->bsp_priv, + SPEED_10); } } @@ -1212,15 +1216,32 @@ static int stmmac_init_phy(struct net_device *dev) */ if (!node || ret) { int addr = priv->plat->phy_addr; - struct phy_device *phydev; - phydev = mdiobus_get_phy(priv->mii, addr); - if (!phydev) { + priv->phydev = mdiobus_get_phy(priv->mii, addr); + if (!priv->phydev) { netdev_err(priv->dev, "no phy at addr %d\n", addr); return -ENODEV; } - ret = phylink_connect_phy(priv->phylink, phydev); + if (priv->plat->phy_intr_en_extn_stm && + priv->plat->phy_intr_en) { + priv->phydev->irq = PHY_MAC_INTERRUPT; + priv->phydev->interrupts = PHY_INTERRUPT_ENABLED; + } + ret = phylink_connect_phy(priv->phylink, priv->phydev); + + if (priv->plat->phy_intr_en_extn_stm && + priv->plat->phy_intr_en) { + if (priv->phydev->drv && + priv->phydev->drv->config_intr && + !priv->phydev->drv->config_intr(priv->phydev)) + netdev_info(priv->dev, + "%s config_phy_intr successful\n", + __func__); + } else { + netdev_info(priv->dev, "stmmac phy polling mode\n"); + priv->phydev->irq = PHY_POLL; + } } if (!priv->plat->pmt) { diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 484097bdc48d8..e6bd4e4d8e876 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -281,10 +281,13 @@ struct plat_stmmacenet_data { int msi_tx_base_vec; bool use_phy_wol; - struct emac_emb_smmu_cb_ctx stmmac_emb_smmu_ctx; int mac2mac_rgmii_speed; bool mac2mac_en; int mac2mac_link; bool mac2mac_88Q5072; + struct emac_emb_smmu_cb_ctx stmmac_emb_smmu_ctx; + bool phy_intr_en_extn_stm; + bool phy_intr_en; + int (*phy_intr_enable)(void *priv); }; #endif |