Commit b9663b7c authored by Voon Weifeng's avatar Voon Weifeng Committed by David S. Miller

net: stmmac: Enable SERDES power up/down sequence

This patch is to enable Intel SERDES power up/down sequence. The SERDES
converts 8/10 bits data to SGMII signal. Below is an example of
HW configuration for SGMII mode. The SERDES is located in the PHY IF
in the diagram below.

<-----------------GBE Controller---------->|<--External PHY chip-->
+----------+         +----+            +---+           +----------+
|   EQoS   | <-GMII->| DW | < ------ > |PHY| <-SGMII-> | External |
|   MAC    |         |xPCS|            |IF |           | PHY      |
+----------+         +----+            +---+           +----------+
       ^               ^                 ^                ^
       |               |                 |                |
       +---------------------MDIO-------------------------+

PHY IF configuration and status registers are accessible through
mdio address 0x15 which is defined as mdio_adhoc_addr. During D0,
The driver will need to power up PHY IF by changing the power state
to P0. Likewise, for D3, the driver sets PHY IF power state to P3.
Signed-off-by: default avatarVoon Weifeng <weifeng.voon@intel.com>
Signed-off-by: default avatarOng Boon Leong <boon.leong.ong@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d7a5502b
...@@ -5,8 +5,13 @@ ...@@ -5,8 +5,13 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include "dwmac-intel.h"
#include "stmmac.h" #include "stmmac.h"
struct intel_priv_data {
int mdio_adhoc_addr; /* mdio address for serdes & etc */
};
/* This struct is used to associate PCI Function of MAC controller on a board, /* This struct is used to associate PCI Function of MAC controller on a board,
* discovered via DMI, with the address of PHY connected to the MAC. The * discovered via DMI, with the address of PHY connected to the MAC. The
* negative value of the address means that MAC controller is not connected * negative value of the address means that MAC controller is not connected
...@@ -49,6 +54,172 @@ static int stmmac_pci_find_phy_addr(struct pci_dev *pdev, ...@@ -49,6 +54,172 @@ static int stmmac_pci_find_phy_addr(struct pci_dev *pdev,
return -ENODEV; return -ENODEV;
} }
static int serdes_status_poll(struct stmmac_priv *priv, int phyaddr,
int phyreg, u32 mask, u32 val)
{
unsigned int retries = 10;
int val_rd;
do {
val_rd = mdiobus_read(priv->mii, phyaddr, phyreg);
if ((val_rd & mask) == (val & mask))
return 0;
udelay(POLL_DELAY_US);
} while (--retries);
return -ETIMEDOUT;
}
static int intel_serdes_powerup(struct net_device *ndev, void *priv_data)
{
struct intel_priv_data *intel_priv = priv_data;
struct stmmac_priv *priv = netdev_priv(ndev);
int serdes_phy_addr = 0;
u32 data = 0;
if (!intel_priv->mdio_adhoc_addr)
return 0;
serdes_phy_addr = intel_priv->mdio_adhoc_addr;
/* assert clk_req */
data = mdiobus_read(priv->mii, serdes_phy_addr,
SERDES_GCR0);
data |= SERDES_PLL_CLK;
mdiobus_write(priv->mii, serdes_phy_addr,
SERDES_GCR0, data);
/* check for clk_ack assertion */
data = serdes_status_poll(priv, serdes_phy_addr,
SERDES_GSR0,
SERDES_PLL_CLK,
SERDES_PLL_CLK);
if (data) {
dev_err(priv->device, "Serdes PLL clk request timeout\n");
return data;
}
/* assert lane reset */
data = mdiobus_read(priv->mii, serdes_phy_addr,
SERDES_GCR0);
data |= SERDES_RST;
mdiobus_write(priv->mii, serdes_phy_addr,
SERDES_GCR0, data);
/* check for assert lane reset reflection */
data = serdes_status_poll(priv, serdes_phy_addr,
SERDES_GSR0,
SERDES_RST,
SERDES_RST);
if (data) {
dev_err(priv->device, "Serdes assert lane reset timeout\n");
return data;
}
/* move power state to P0 */
data = mdiobus_read(priv->mii, serdes_phy_addr,
SERDES_GCR0);
data &= ~SERDES_PWR_ST_MASK;
data |= SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT;
mdiobus_write(priv->mii, serdes_phy_addr,
SERDES_GCR0, data);
/* Check for P0 state */
data = serdes_status_poll(priv, serdes_phy_addr,
SERDES_GSR0,
SERDES_PWR_ST_MASK,
SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT);
if (data) {
dev_err(priv->device, "Serdes power state P0 timeout.\n");
return data;
}
return 0;
}
static void intel_serdes_powerdown(struct net_device *ndev, void *intel_data)
{
struct intel_priv_data *intel_priv = intel_data;
struct stmmac_priv *priv = netdev_priv(ndev);
int serdes_phy_addr = 0;
u32 data = 0;
if (!intel_priv->mdio_adhoc_addr)
return;
serdes_phy_addr = intel_priv->mdio_adhoc_addr;
/* move power state to P3 */
data = mdiobus_read(priv->mii, serdes_phy_addr,
SERDES_GCR0);
data &= ~SERDES_PWR_ST_MASK;
data |= SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT;
mdiobus_write(priv->mii, serdes_phy_addr,
SERDES_GCR0, data);
/* Check for P3 state */
data = serdes_status_poll(priv, serdes_phy_addr,
SERDES_GSR0,
SERDES_PWR_ST_MASK,
SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT);
if (data) {
dev_err(priv->device, "Serdes power state P3 timeout\n");
return;
}
/* de-assert clk_req */
data = mdiobus_read(priv->mii, serdes_phy_addr,
SERDES_GCR0);
data &= ~SERDES_PLL_CLK;
mdiobus_write(priv->mii, serdes_phy_addr,
SERDES_GCR0, data);
/* check for clk_ack de-assert */
data = serdes_status_poll(priv, serdes_phy_addr,
SERDES_GSR0,
SERDES_PLL_CLK,
(u32)~SERDES_PLL_CLK);
if (data) {
dev_err(priv->device, "Serdes PLL clk de-assert timeout\n");
return;
}
/* de-assert lane reset */
data = mdiobus_read(priv->mii, serdes_phy_addr,
SERDES_GCR0);
data &= ~SERDES_RST;
mdiobus_write(priv->mii, serdes_phy_addr,
SERDES_GCR0, data);
/* check for de-assert lane reset reflection */
data = serdes_status_poll(priv, serdes_phy_addr,
SERDES_GSR0,
SERDES_RST,
(u32)~SERDES_RST);
if (data) {
dev_err(priv->device, "Serdes de-assert lane reset timeout\n");
return;
}
}
static void common_default_data(struct plat_stmmacenet_data *plat) static void common_default_data(struct plat_stmmacenet_data *plat)
{ {
plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
...@@ -189,6 +360,9 @@ static int ehl_sgmii_data(struct pci_dev *pdev, ...@@ -189,6 +360,9 @@ static int ehl_sgmii_data(struct pci_dev *pdev,
plat->phy_addr = 0; plat->phy_addr = 0;
plat->phy_interface = PHY_INTERFACE_MODE_SGMII; plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
plat->serdes_powerup = intel_serdes_powerup;
plat->serdes_powerdown = intel_serdes_powerdown;
return ehl_common_data(pdev, plat); return ehl_common_data(pdev, plat);
} }
...@@ -233,6 +407,8 @@ static int ehl_pse0_sgmii1g_data(struct pci_dev *pdev, ...@@ -233,6 +407,8 @@ static int ehl_pse0_sgmii1g_data(struct pci_dev *pdev,
struct plat_stmmacenet_data *plat) struct plat_stmmacenet_data *plat)
{ {
plat->phy_interface = PHY_INTERFACE_MODE_SGMII; plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
plat->serdes_powerup = intel_serdes_powerup;
plat->serdes_powerdown = intel_serdes_powerdown;
return ehl_pse0_common_data(pdev, plat); return ehl_pse0_common_data(pdev, plat);
} }
...@@ -263,6 +439,8 @@ static int ehl_pse1_sgmii1g_data(struct pci_dev *pdev, ...@@ -263,6 +439,8 @@ static int ehl_pse1_sgmii1g_data(struct pci_dev *pdev,
struct plat_stmmacenet_data *plat) struct plat_stmmacenet_data *plat)
{ {
plat->phy_interface = PHY_INTERFACE_MODE_SGMII; plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
plat->serdes_powerup = intel_serdes_powerup;
plat->serdes_powerdown = intel_serdes_powerdown;
return ehl_pse1_common_data(pdev, plat); return ehl_pse1_common_data(pdev, plat);
} }
...@@ -291,6 +469,8 @@ static int tgl_sgmii_data(struct pci_dev *pdev, ...@@ -291,6 +469,8 @@ static int tgl_sgmii_data(struct pci_dev *pdev,
plat->bus_id = 1; plat->bus_id = 1;
plat->phy_addr = 0; plat->phy_addr = 0;
plat->phy_interface = PHY_INTERFACE_MODE_SGMII; plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
plat->serdes_powerup = intel_serdes_powerup;
plat->serdes_powerdown = intel_serdes_powerdown;
return tgl_common_data(pdev, plat); return tgl_common_data(pdev, plat);
} }
...@@ -417,11 +597,17 @@ static int intel_eth_pci_probe(struct pci_dev *pdev, ...@@ -417,11 +597,17 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id) const struct pci_device_id *id)
{ {
struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data; struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data;
struct intel_priv_data *intel_priv;
struct plat_stmmacenet_data *plat; struct plat_stmmacenet_data *plat;
struct stmmac_resources res; struct stmmac_resources res;
int i; int i;
int ret; int ret;
intel_priv = devm_kzalloc(&pdev->dev, sizeof(*intel_priv),
GFP_KERNEL);
if (!intel_priv)
return -ENOMEM;
plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
if (!plat) if (!plat)
return -ENOMEM; return -ENOMEM;
...@@ -457,6 +643,9 @@ static int intel_eth_pci_probe(struct pci_dev *pdev, ...@@ -457,6 +643,9 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
pci_set_master(pdev); pci_set_master(pdev);
plat->bsp_priv = intel_priv;
intel_priv->mdio_adhoc_addr = 0x15;
ret = info->setup(pdev, plat); ret = info->setup(pdev, plat);
if (ret) if (ret)
return ret; return ret;
......
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2020, Intel Corporation
* DWMAC Intel header file
*/
#ifndef __DWMAC_INTEL_H__
#define __DWMAC_INTEL_H__
#define POLL_DELAY_US 8
/* SERDES Register */
#define SERDES_GSR0 0x5 /* Global Status Reg0 */
#define SERDES_GCR0 0xb /* Global Configuration Reg0 */
/* SERDES defines */
#define SERDES_PLL_CLK BIT(0) /* PLL clk valid signal */
#define SERDES_RST BIT(2) /* Serdes Reset */
#define SERDES_PWR_ST_MASK GENMASK(6, 4) /* Serdes Power state*/
#define SERDES_PWR_ST_SHIFT 4
#define SERDES_PWR_ST_P0 0x0
#define SERDES_PWR_ST_P3 0x3
#endif /* __DWMAC_INTEL_H__ */
...@@ -4986,6 +4986,14 @@ int stmmac_dvr_probe(struct device *device, ...@@ -4986,6 +4986,14 @@ int stmmac_dvr_probe(struct device *device,
goto error_netdev_register; goto error_netdev_register;
} }
if (priv->plat->serdes_powerup) {
ret = priv->plat->serdes_powerup(ndev,
priv->plat->bsp_priv);
if (ret < 0)
return ret;
}
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
stmmac_init_fs(ndev); stmmac_init_fs(ndev);
#endif #endif
...@@ -5029,6 +5037,9 @@ int stmmac_dvr_remove(struct device *dev) ...@@ -5029,6 +5037,9 @@ int stmmac_dvr_remove(struct device *dev)
stmmac_stop_all_dma(priv); stmmac_stop_all_dma(priv);
if (priv->plat->serdes_powerdown)
priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv);
stmmac_mac_set(priv, priv->ioaddr, false); stmmac_mac_set(priv, priv->ioaddr, false);
netif_carrier_off(ndev); netif_carrier_off(ndev);
unregister_netdev(ndev); unregister_netdev(ndev);
...@@ -5081,6 +5092,9 @@ int stmmac_suspend(struct device *dev) ...@@ -5081,6 +5092,9 @@ int stmmac_suspend(struct device *dev)
/* Stop TX/RX DMA */ /* Stop TX/RX DMA */
stmmac_stop_all_dma(priv); stmmac_stop_all_dma(priv);
if (priv->plat->serdes_powerdown)
priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv);
/* Enable Power down mode by programming the PMT regs */ /* Enable Power down mode by programming the PMT regs */
if (device_may_wakeup(priv->device)) { if (device_may_wakeup(priv->device)) {
stmmac_pmt(priv, priv->hw, priv->wolopts); stmmac_pmt(priv, priv->hw, priv->wolopts);
...@@ -5143,6 +5157,7 @@ int stmmac_resume(struct device *dev) ...@@ -5143,6 +5157,7 @@ int stmmac_resume(struct device *dev)
{ {
struct net_device *ndev = dev_get_drvdata(dev); struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev); struct stmmac_priv *priv = netdev_priv(ndev);
int ret;
if (!netif_running(ndev)) if (!netif_running(ndev))
return 0; return 0;
...@@ -5170,6 +5185,14 @@ int stmmac_resume(struct device *dev) ...@@ -5170,6 +5185,14 @@ int stmmac_resume(struct device *dev)
stmmac_mdio_reset(priv->mii); stmmac_mdio_reset(priv->mii);
} }
if (priv->plat->serdes_powerup) {
ret = priv->plat->serdes_powerup(ndev,
priv->plat->bsp_priv);
if (ret < 0)
return ret;
}
netif_device_attach(ndev); netif_device_attach(ndev);
mutex_lock(&priv->lock); mutex_lock(&priv->lock);
......
...@@ -177,6 +177,8 @@ struct plat_stmmacenet_data { ...@@ -177,6 +177,8 @@ struct plat_stmmacenet_data {
struct stmmac_rxq_cfg rx_queues_cfg[MTL_MAX_RX_QUEUES]; struct stmmac_rxq_cfg rx_queues_cfg[MTL_MAX_RX_QUEUES];
struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES]; struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES];
void (*fix_mac_speed)(void *priv, unsigned int speed); void (*fix_mac_speed)(void *priv, unsigned int speed);
int (*serdes_powerup)(struct net_device *ndev, void *priv);
void (*serdes_powerdown)(struct net_device *ndev, void *priv);
int (*init)(struct platform_device *pdev, void *priv); int (*init)(struct platform_device *pdev, void *priv);
void (*exit)(struct platform_device *pdev, void *priv); void (*exit)(struct platform_device *pdev, void *priv);
struct mac_device_info *(*setup)(void *priv); struct mac_device_info *(*setup)(void *priv);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment