Skip to content

Commit b9663b7

Browse files
wvoondavem330
authored andcommitted
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: Voon Weifeng <[email protected]> Signed-off-by: Ong Boon Leong <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent d7a5502 commit b9663b7

File tree

4 files changed

+237
-0
lines changed

4 files changed

+237
-0
lines changed

drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
#include <linux/clk-provider.h>
66
#include <linux/pci.h>
77
#include <linux/dmi.h>
8+
#include "dwmac-intel.h"
89
#include "stmmac.h"
910

11+
struct intel_priv_data {
12+
int mdio_adhoc_addr; /* mdio address for serdes & etc */
13+
};
14+
1015
/* This struct is used to associate PCI Function of MAC controller on a board,
1116
* discovered via DMI, with the address of PHY connected to the MAC. The
1217
* 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,
4954
return -ENODEV;
5055
}
5156

57+
static int serdes_status_poll(struct stmmac_priv *priv, int phyaddr,
58+
int phyreg, u32 mask, u32 val)
59+
{
60+
unsigned int retries = 10;
61+
int val_rd;
62+
63+
do {
64+
val_rd = mdiobus_read(priv->mii, phyaddr, phyreg);
65+
if ((val_rd & mask) == (val & mask))
66+
return 0;
67+
udelay(POLL_DELAY_US);
68+
} while (--retries);
69+
70+
return -ETIMEDOUT;
71+
}
72+
73+
static int intel_serdes_powerup(struct net_device *ndev, void *priv_data)
74+
{
75+
struct intel_priv_data *intel_priv = priv_data;
76+
struct stmmac_priv *priv = netdev_priv(ndev);
77+
int serdes_phy_addr = 0;
78+
u32 data = 0;
79+
80+
if (!intel_priv->mdio_adhoc_addr)
81+
return 0;
82+
83+
serdes_phy_addr = intel_priv->mdio_adhoc_addr;
84+
85+
/* assert clk_req */
86+
data = mdiobus_read(priv->mii, serdes_phy_addr,
87+
SERDES_GCR0);
88+
89+
data |= SERDES_PLL_CLK;
90+
91+
mdiobus_write(priv->mii, serdes_phy_addr,
92+
SERDES_GCR0, data);
93+
94+
/* check for clk_ack assertion */
95+
data = serdes_status_poll(priv, serdes_phy_addr,
96+
SERDES_GSR0,
97+
SERDES_PLL_CLK,
98+
SERDES_PLL_CLK);
99+
100+
if (data) {
101+
dev_err(priv->device, "Serdes PLL clk request timeout\n");
102+
return data;
103+
}
104+
105+
/* assert lane reset */
106+
data = mdiobus_read(priv->mii, serdes_phy_addr,
107+
SERDES_GCR0);
108+
109+
data |= SERDES_RST;
110+
111+
mdiobus_write(priv->mii, serdes_phy_addr,
112+
SERDES_GCR0, data);
113+
114+
/* check for assert lane reset reflection */
115+
data = serdes_status_poll(priv, serdes_phy_addr,
116+
SERDES_GSR0,
117+
SERDES_RST,
118+
SERDES_RST);
119+
120+
if (data) {
121+
dev_err(priv->device, "Serdes assert lane reset timeout\n");
122+
return data;
123+
}
124+
125+
/* move power state to P0 */
126+
data = mdiobus_read(priv->mii, serdes_phy_addr,
127+
SERDES_GCR0);
128+
129+
data &= ~SERDES_PWR_ST_MASK;
130+
data |= SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT;
131+
132+
mdiobus_write(priv->mii, serdes_phy_addr,
133+
SERDES_GCR0, data);
134+
135+
/* Check for P0 state */
136+
data = serdes_status_poll(priv, serdes_phy_addr,
137+
SERDES_GSR0,
138+
SERDES_PWR_ST_MASK,
139+
SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT);
140+
141+
if (data) {
142+
dev_err(priv->device, "Serdes power state P0 timeout.\n");
143+
return data;
144+
}
145+
146+
return 0;
147+
}
148+
149+
static void intel_serdes_powerdown(struct net_device *ndev, void *intel_data)
150+
{
151+
struct intel_priv_data *intel_priv = intel_data;
152+
struct stmmac_priv *priv = netdev_priv(ndev);
153+
int serdes_phy_addr = 0;
154+
u32 data = 0;
155+
156+
if (!intel_priv->mdio_adhoc_addr)
157+
return;
158+
159+
serdes_phy_addr = intel_priv->mdio_adhoc_addr;
160+
161+
/* move power state to P3 */
162+
data = mdiobus_read(priv->mii, serdes_phy_addr,
163+
SERDES_GCR0);
164+
165+
data &= ~SERDES_PWR_ST_MASK;
166+
data |= SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT;
167+
168+
mdiobus_write(priv->mii, serdes_phy_addr,
169+
SERDES_GCR0, data);
170+
171+
/* Check for P3 state */
172+
data = serdes_status_poll(priv, serdes_phy_addr,
173+
SERDES_GSR0,
174+
SERDES_PWR_ST_MASK,
175+
SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT);
176+
177+
if (data) {
178+
dev_err(priv->device, "Serdes power state P3 timeout\n");
179+
return;
180+
}
181+
182+
/* de-assert clk_req */
183+
data = mdiobus_read(priv->mii, serdes_phy_addr,
184+
SERDES_GCR0);
185+
186+
data &= ~SERDES_PLL_CLK;
187+
188+
mdiobus_write(priv->mii, serdes_phy_addr,
189+
SERDES_GCR0, data);
190+
191+
/* check for clk_ack de-assert */
192+
data = serdes_status_poll(priv, serdes_phy_addr,
193+
SERDES_GSR0,
194+
SERDES_PLL_CLK,
195+
(u32)~SERDES_PLL_CLK);
196+
197+
if (data) {
198+
dev_err(priv->device, "Serdes PLL clk de-assert timeout\n");
199+
return;
200+
}
201+
202+
/* de-assert lane reset */
203+
data = mdiobus_read(priv->mii, serdes_phy_addr,
204+
SERDES_GCR0);
205+
206+
data &= ~SERDES_RST;
207+
208+
mdiobus_write(priv->mii, serdes_phy_addr,
209+
SERDES_GCR0, data);
210+
211+
/* check for de-assert lane reset reflection */
212+
data = serdes_status_poll(priv, serdes_phy_addr,
213+
SERDES_GSR0,
214+
SERDES_RST,
215+
(u32)~SERDES_RST);
216+
217+
if (data) {
218+
dev_err(priv->device, "Serdes de-assert lane reset timeout\n");
219+
return;
220+
}
221+
}
222+
52223
static void common_default_data(struct plat_stmmacenet_data *plat)
53224
{
54225
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,
189360
plat->phy_addr = 0;
190361
plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
191362

363+
plat->serdes_powerup = intel_serdes_powerup;
364+
plat->serdes_powerdown = intel_serdes_powerdown;
365+
192366
return ehl_common_data(pdev, plat);
193367
}
194368

@@ -233,6 +407,8 @@ static int ehl_pse0_sgmii1g_data(struct pci_dev *pdev,
233407
struct plat_stmmacenet_data *plat)
234408
{
235409
plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
410+
plat->serdes_powerup = intel_serdes_powerup;
411+
plat->serdes_powerdown = intel_serdes_powerdown;
236412
return ehl_pse0_common_data(pdev, plat);
237413
}
238414

@@ -263,6 +439,8 @@ static int ehl_pse1_sgmii1g_data(struct pci_dev *pdev,
263439
struct plat_stmmacenet_data *plat)
264440
{
265441
plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
442+
plat->serdes_powerup = intel_serdes_powerup;
443+
plat->serdes_powerdown = intel_serdes_powerdown;
266444
return ehl_pse1_common_data(pdev, plat);
267445
}
268446

@@ -291,6 +469,8 @@ static int tgl_sgmii_data(struct pci_dev *pdev,
291469
plat->bus_id = 1;
292470
plat->phy_addr = 0;
293471
plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
472+
plat->serdes_powerup = intel_serdes_powerup;
473+
plat->serdes_powerdown = intel_serdes_powerdown;
294474
return tgl_common_data(pdev, plat);
295475
}
296476

@@ -417,11 +597,17 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
417597
const struct pci_device_id *id)
418598
{
419599
struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data;
600+
struct intel_priv_data *intel_priv;
420601
struct plat_stmmacenet_data *plat;
421602
struct stmmac_resources res;
422603
int i;
423604
int ret;
424605

606+
intel_priv = devm_kzalloc(&pdev->dev, sizeof(*intel_priv),
607+
GFP_KERNEL);
608+
if (!intel_priv)
609+
return -ENOMEM;
610+
425611
plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
426612
if (!plat)
427613
return -ENOMEM;
@@ -457,6 +643,9 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
457643

458644
pci_set_master(pdev);
459645

646+
plat->bsp_priv = intel_priv;
647+
intel_priv->mdio_adhoc_addr = 0x15;
648+
460649
ret = info->setup(pdev, plat);
461650
if (ret)
462651
return ret;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/* Copyright (c) 2020, Intel Corporation
3+
* DWMAC Intel header file
4+
*/
5+
6+
#ifndef __DWMAC_INTEL_H__
7+
#define __DWMAC_INTEL_H__
8+
9+
#define POLL_DELAY_US 8
10+
11+
/* SERDES Register */
12+
#define SERDES_GSR0 0x5 /* Global Status Reg0 */
13+
#define SERDES_GCR0 0xb /* Global Configuration Reg0 */
14+
15+
/* SERDES defines */
16+
#define SERDES_PLL_CLK BIT(0) /* PLL clk valid signal */
17+
#define SERDES_RST BIT(2) /* Serdes Reset */
18+
#define SERDES_PWR_ST_MASK GENMASK(6, 4) /* Serdes Power state*/
19+
#define SERDES_PWR_ST_SHIFT 4
20+
#define SERDES_PWR_ST_P0 0x0
21+
#define SERDES_PWR_ST_P3 0x3
22+
23+
#endif /* __DWMAC_INTEL_H__ */

drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4986,6 +4986,14 @@ int stmmac_dvr_probe(struct device *device,
49864986
goto error_netdev_register;
49874987
}
49884988

4989+
if (priv->plat->serdes_powerup) {
4990+
ret = priv->plat->serdes_powerup(ndev,
4991+
priv->plat->bsp_priv);
4992+
4993+
if (ret < 0)
4994+
return ret;
4995+
}
4996+
49894997
#ifdef CONFIG_DEBUG_FS
49904998
stmmac_init_fs(ndev);
49914999
#endif
@@ -5029,6 +5037,9 @@ int stmmac_dvr_remove(struct device *dev)
50295037

50305038
stmmac_stop_all_dma(priv);
50315039

5040+
if (priv->plat->serdes_powerdown)
5041+
priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv);
5042+
50325043
stmmac_mac_set(priv, priv->ioaddr, false);
50335044
netif_carrier_off(ndev);
50345045
unregister_netdev(ndev);
@@ -5081,6 +5092,9 @@ int stmmac_suspend(struct device *dev)
50815092
/* Stop TX/RX DMA */
50825093
stmmac_stop_all_dma(priv);
50835094

5095+
if (priv->plat->serdes_powerdown)
5096+
priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv);
5097+
50845098
/* Enable Power down mode by programming the PMT regs */
50855099
if (device_may_wakeup(priv->device)) {
50865100
stmmac_pmt(priv, priv->hw, priv->wolopts);
@@ -5143,6 +5157,7 @@ int stmmac_resume(struct device *dev)
51435157
{
51445158
struct net_device *ndev = dev_get_drvdata(dev);
51455159
struct stmmac_priv *priv = netdev_priv(ndev);
5160+
int ret;
51465161

51475162
if (!netif_running(ndev))
51485163
return 0;
@@ -5170,6 +5185,14 @@ int stmmac_resume(struct device *dev)
51705185
stmmac_mdio_reset(priv->mii);
51715186
}
51725187

5188+
if (priv->plat->serdes_powerup) {
5189+
ret = priv->plat->serdes_powerup(ndev,
5190+
priv->plat->bsp_priv);
5191+
5192+
if (ret < 0)
5193+
return ret;
5194+
}
5195+
51735196
netif_device_attach(ndev);
51745197

51755198
mutex_lock(&priv->lock);

include/linux/stmmac.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ struct plat_stmmacenet_data {
177177
struct stmmac_rxq_cfg rx_queues_cfg[MTL_MAX_RX_QUEUES];
178178
struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES];
179179
void (*fix_mac_speed)(void *priv, unsigned int speed);
180+
int (*serdes_powerup)(struct net_device *ndev, void *priv);
181+
void (*serdes_powerdown)(struct net_device *ndev, void *priv);
180182
int (*init)(struct platform_device *pdev, void *priv);
181183
void (*exit)(struct platform_device *pdev, void *priv);
182184
struct mac_device_info *(*setup)(void *priv);

0 commit comments

Comments
 (0)