aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2022-12-06 14:38:51 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2022-12-06 14:38:51 -0800
commit86f6506147e2ed1a725bb97484a72e5888e91736 (patch)
tree0f33da399c2e71c2eb1e51df354031cdfec97d81
parent51ab7643922fd1069390943fe803760506e6bd61 (diff)
parent88a24e4ecdf5774ef19b95ec60d7faeaec6d95a8 (diff)
Merge "net: stmmac: Add support for PHY interrupts" into wip-er12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c136
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c64
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c33
-rw-r--r--include/linux/stmmac.h5
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, &ethqos->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(&ethqos->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(&ethqos->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(&ethqos->pdev->dev);
if (ret) {
ETHQOSERR("ethqos_init_pinctrl failed");
return ret;
}
+ ret = setup_gpio_input_common(&ethqos->pdev->dev,
+ "qcom,phy-intr-redirect",
+ &ethqos->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