Skip to content

Commit d765955

Browse files
cavagiudavem330
authored andcommitted
stmmac: add the Energy Efficient Ethernet support
This patch adds the Energy Efficient Ethernet support to the stmmac. Please see the driver's documentation for further details about this support in the driver. Thanks also goes to Rayagond Kokatanur for his first implementation. Note: to clearly manage and expose the lpi interrupt status and eee ethtool stats I've had to do some modifications to the driver's design and I found really useful to move other parts of the code (e.g. mmc irq stat) in the main directly. So this means that some core has been reworked to introduce the EEE. v1: initial patch v2: fixed some sparse issues (typos) v3: erroneously sent the v2 renamed as v3 v4: o Fixed the return value of the stmmac_eee_init as suggested by D.Miller o Totally reviewed the ethtool support for EEE o Added a new internal parameter to tune the SW timer for TX LPI. v5: do not change any eee setting in case of the stmmac_ethtool_op_set_eee fails (it has to return -EOPNOTSUPP in that case). Signed-off-by: Giuseppe Cavallaro <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0ec2ccd commit d765955

File tree

9 files changed

+372
-18
lines changed

9 files changed

+372
-18
lines changed

drivers/net/ethernet/stmicro/stmmac/common.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,16 @@ struct stmmac_extra_stats {
9595
unsigned long poll_n;
9696
unsigned long sched_timer_n;
9797
unsigned long normal_irq_n;
98+
unsigned long mmc_tx_irq_n;
99+
unsigned long mmc_rx_irq_n;
100+
unsigned long mmc_rx_csum_offload_irq_n;
101+
/* EEE */
102+
unsigned long irq_receive_pmt_irq_n;
103+
unsigned long irq_tx_path_in_lpi_mode_n;
104+
unsigned long irq_tx_path_exit_lpi_mode_n;
105+
unsigned long irq_rx_path_in_lpi_mode_n;
106+
unsigned long irq_rx_path_exit_lpi_mode_n;
107+
unsigned long phy_eee_wakeup_error_n;
98108
};
99109

100110
/* CSR Frequency Access Defines*/
@@ -162,6 +172,17 @@ enum tx_dma_irq_status {
162172
handle_tx_rx = 3,
163173
};
164174

175+
enum core_specific_irq_mask {
176+
core_mmc_tx_irq = 1,
177+
core_mmc_rx_irq = 2,
178+
core_mmc_rx_csum_offload_irq = 4,
179+
core_irq_receive_pmt_irq = 8,
180+
core_irq_tx_path_in_lpi_mode = 16,
181+
core_irq_tx_path_exit_lpi_mode = 32,
182+
core_irq_rx_path_in_lpi_mode = 64,
183+
core_irq_rx_path_exit_lpi_mode = 128,
184+
};
185+
165186
/* DMA HW capabilities */
166187
struct dma_features {
167188
unsigned int mbps_10_100;
@@ -208,6 +229,10 @@ struct dma_features {
208229
#define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */
209230
#define MAC_RNABLE_RX 0x00000004 /* Receiver Enable */
210231

232+
/* Default LPI timers */
233+
#define STMMAC_DEFAULT_LIT_LS_TIMER 0x3E8
234+
#define STMMAC_DEFAULT_TWT_LS_TIMER 0x0
235+
211236
struct stmmac_desc_ops {
212237
/* DMA RX descriptor ring initialization */
213238
void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size,
@@ -278,7 +303,7 @@ struct stmmac_ops {
278303
/* Dump MAC registers */
279304
void (*dump_regs) (void __iomem *ioaddr);
280305
/* Handle extra events on specific interrupts hw dependent */
281-
void (*host_irq_status) (void __iomem *ioaddr);
306+
int (*host_irq_status) (void __iomem *ioaddr);
282307
/* Multicast filter setting */
283308
void (*set_filter) (struct net_device *dev, int id);
284309
/* Flow control setting */
@@ -291,6 +316,10 @@ struct stmmac_ops {
291316
unsigned int reg_n);
292317
void (*get_umac_addr) (void __iomem *ioaddr, unsigned char *addr,
293318
unsigned int reg_n);
319+
void (*set_eee_mode) (void __iomem *ioaddr);
320+
void (*reset_eee_mode) (void __iomem *ioaddr);
321+
void (*set_eee_timer) (void __iomem *ioaddr, int ls, int tw);
322+
void (*set_eee_pls) (void __iomem *ioaddr, int link);
294323
};
295324

296325
struct mac_link {

drivers/net/ethernet/stmicro/stmmac/dwmac1000.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */
3838
enum dwmac1000_irq_status {
39+
lpiis_irq = 0x400,
3940
time_stamp_irq = 0x0200,
4041
mmc_rx_csum_offload_irq = 0x0080,
4142
mmc_tx_irq = 0x0040,
@@ -60,6 +61,25 @@ enum power_event {
6061
power_down = 0x00000001,
6162
};
6263

64+
/* Energy Efficient Ethernet (EEE)
65+
*
66+
* LPI status, timer and control register offset
67+
*/
68+
#define LPI_CTRL_STATUS 0x0030
69+
#define LPI_TIMER_CTRL 0x0034
70+
71+
/* LPI control and status defines */
72+
#define LPI_CTRL_STATUS_LPITXA 0x00080000 /* Enable LPI TX Automate */
73+
#define LPI_CTRL_STATUS_PLSEN 0x00040000 /* Enable PHY Link Status */
74+
#define LPI_CTRL_STATUS_PLS 0x00020000 /* PHY Link Status */
75+
#define LPI_CTRL_STATUS_LPIEN 0x00010000 /* LPI Enable */
76+
#define LPI_CTRL_STATUS_RLPIST 0x00000200 /* Receive LPI state */
77+
#define LPI_CTRL_STATUS_TLPIST 0x00000100 /* Transmit LPI state */
78+
#define LPI_CTRL_STATUS_RLPIEX 0x00000008 /* Receive LPI Exit */
79+
#define LPI_CTRL_STATUS_RLPIEN 0x00000004 /* Receive LPI Entry */
80+
#define LPI_CTRL_STATUS_TLPIEX 0x00000002 /* Transmit LPI Exit */
81+
#define LPI_CTRL_STATUS_TLPIEN 0x00000001 /* Transmit LPI Entry */
82+
6383
/* GMAC HW ADDR regs */
6484
#define GMAC_ADDR_HIGH(reg) (((reg > 15) ? 0x00000800 : 0x00000040) + \
6585
(reg * 8))

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

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -194,26 +194,107 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode)
194194
}
195195

196196

197-
static void dwmac1000_irq_status(void __iomem *ioaddr)
197+
static int dwmac1000_irq_status(void __iomem *ioaddr)
198198
{
199199
u32 intr_status = readl(ioaddr + GMAC_INT_STATUS);
200+
int status = 0;
200201

201202
/* Not used events (e.g. MMC interrupts) are not handled. */
202-
if ((intr_status & mmc_tx_irq))
203-
CHIP_DBG(KERN_DEBUG "GMAC: MMC tx interrupt: 0x%08x\n",
203+
if ((intr_status & mmc_tx_irq)) {
204+
CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n",
204205
readl(ioaddr + GMAC_MMC_TX_INTR));
205-
if (unlikely(intr_status & mmc_rx_irq))
206-
CHIP_DBG(KERN_DEBUG "GMAC: MMC rx interrupt: 0x%08x\n",
206+
status |= core_mmc_tx_irq;
207+
}
208+
if (unlikely(intr_status & mmc_rx_irq)) {
209+
CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n",
207210
readl(ioaddr + GMAC_MMC_RX_INTR));
208-
if (unlikely(intr_status & mmc_rx_csum_offload_irq))
209-
CHIP_DBG(KERN_DEBUG "GMAC: MMC rx csum offload: 0x%08x\n",
211+
status |= core_mmc_rx_irq;
212+
}
213+
if (unlikely(intr_status & mmc_rx_csum_offload_irq)) {
214+
CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n",
210215
readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD));
216+
status |= core_mmc_rx_csum_offload_irq;
217+
}
211218
if (unlikely(intr_status & pmt_irq)) {
212-
CHIP_DBG(KERN_DEBUG "GMAC: received Magic frame\n");
219+
CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n");
213220
/* clear the PMT bits 5 and 6 by reading the PMT
214221
* status register. */
215222
readl(ioaddr + GMAC_PMT);
223+
status |= core_irq_receive_pmt_irq;
216224
}
225+
/* MAC trx/rx EEE LPI entry/exit interrupts */
226+
if (intr_status & lpiis_irq) {
227+
/* Clean LPI interrupt by reading the Reg 12 */
228+
u32 lpi_status = readl(ioaddr + LPI_CTRL_STATUS);
229+
230+
if (lpi_status & LPI_CTRL_STATUS_TLPIEN) {
231+
CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n");
232+
status |= core_irq_tx_path_in_lpi_mode;
233+
}
234+
if (lpi_status & LPI_CTRL_STATUS_TLPIEX) {
235+
CHIP_DBG(KERN_INFO "GMAC TX exit from LPI\n");
236+
status |= core_irq_tx_path_exit_lpi_mode;
237+
}
238+
if (lpi_status & LPI_CTRL_STATUS_RLPIEN) {
239+
CHIP_DBG(KERN_INFO "GMAC RX entered in LPI\n");
240+
status |= core_irq_rx_path_in_lpi_mode;
241+
}
242+
if (lpi_status & LPI_CTRL_STATUS_RLPIEX) {
243+
CHIP_DBG(KERN_INFO "GMAC RX exit from LPI\n");
244+
status |= core_irq_rx_path_exit_lpi_mode;
245+
}
246+
}
247+
248+
return status;
249+
}
250+
251+
static void dwmac1000_set_eee_mode(void __iomem *ioaddr)
252+
{
253+
u32 value;
254+
255+
/* Enable the link status receive on RGMII, SGMII ore SMII
256+
* receive path and instruct the transmit to enter in LPI
257+
* state. */
258+
value = readl(ioaddr + LPI_CTRL_STATUS);
259+
value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
260+
writel(value, ioaddr + LPI_CTRL_STATUS);
261+
}
262+
263+
static void dwmac1000_reset_eee_mode(void __iomem *ioaddr)
264+
{
265+
u32 value;
266+
267+
value = readl(ioaddr + LPI_CTRL_STATUS);
268+
value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA);
269+
writel(value, ioaddr + LPI_CTRL_STATUS);
270+
}
271+
272+
static void dwmac1000_set_eee_pls(void __iomem *ioaddr, int link)
273+
{
274+
u32 value;
275+
276+
value = readl(ioaddr + LPI_CTRL_STATUS);
277+
278+
if (link)
279+
value |= LPI_CTRL_STATUS_PLS;
280+
else
281+
value &= ~LPI_CTRL_STATUS_PLS;
282+
283+
writel(value, ioaddr + LPI_CTRL_STATUS);
284+
}
285+
286+
static void dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw)
287+
{
288+
int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16);
289+
290+
/* Program the timers in the LPI timer control register:
291+
* LS: minimum time (ms) for which the link
292+
* status from PHY should be ok before transmitting
293+
* the LPI pattern.
294+
* TW: minimum time (us) for which the core waits
295+
* after it has stopped transmitting the LPI pattern.
296+
*/
297+
writel(value, ioaddr + LPI_TIMER_CTRL);
217298
}
218299

219300
static const struct stmmac_ops dwmac1000_ops = {
@@ -226,6 +307,10 @@ static const struct stmmac_ops dwmac1000_ops = {
226307
.pmt = dwmac1000_pmt,
227308
.set_umac_addr = dwmac1000_set_umac_addr,
228309
.get_umac_addr = dwmac1000_get_umac_addr,
310+
.set_eee_mode = dwmac1000_set_eee_mode,
311+
.reset_eee_mode = dwmac1000_reset_eee_mode,
312+
.set_eee_timer = dwmac1000_set_eee_timer,
313+
.set_eee_pls = dwmac1000_set_eee_pls,
229314
};
230315

231316
struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ static int dwmac100_rx_ipc_enable(void __iomem *ioaddr)
7272
return 0;
7373
}
7474

75-
static void dwmac100_irq_status(void __iomem *ioaddr)
75+
static int dwmac100_irq_status(void __iomem *ioaddr)
7676
{
77-
return;
77+
return 0;
7878
}
7979

8080
static void dwmac100_set_umac_addr(void __iomem *ioaddr, unsigned char *addr,

drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
#define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL)
7171

7272
/* DMA Status register defines */
73+
#define DMA_STATUS_GLPII 0x40000000 /* GMAC LPI interrupt */
7374
#define DMA_STATUS_GPI 0x10000000 /* PMT interrupt */
7475
#define DMA_STATUS_GMI 0x08000000 /* MMC interrupt */
7576
#define DMA_STATUS_GLI 0x04000000 /* GMAC Line interface int */

drivers/net/ethernet/stmicro/stmmac/stmmac.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ struct stmmac_priv {
8787
#endif
8888
int clk_csr;
8989
int synopsys_id;
90+
struct timer_list eee_ctrl_timer;
91+
bool tx_path_in_lpi_mode;
92+
int lpi_irq;
93+
int eee_enabled;
94+
int eee_active;
95+
int tx_lpi_timer;
9096
};
9197

9298
extern int phyaddr;
@@ -104,6 +110,8 @@ int stmmac_dvr_remove(struct net_device *ndev);
104110
struct stmmac_priv *stmmac_dvr_probe(struct device *device,
105111
struct plat_stmmacenet_data *plat_dat,
106112
void __iomem *addr);
113+
void stmmac_disable_eee_mode(struct stmmac_priv *priv);
114+
bool stmmac_eee_init(struct stmmac_priv *priv);
107115

108116
#ifdef CONFIG_HAVE_CLK
109117
static inline int stmmac_clk_enable(struct stmmac_priv *priv)

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
9393
STMMAC_STAT(poll_n),
9494
STMMAC_STAT(sched_timer_n),
9595
STMMAC_STAT(normal_irq_n),
96+
STMMAC_STAT(normal_irq_n),
97+
STMMAC_STAT(mmc_tx_irq_n),
98+
STMMAC_STAT(mmc_rx_irq_n),
99+
STMMAC_STAT(mmc_rx_csum_offload_irq_n),
100+
STMMAC_STAT(irq_receive_pmt_irq_n),
101+
STMMAC_STAT(irq_tx_path_in_lpi_mode_n),
102+
STMMAC_STAT(irq_tx_path_exit_lpi_mode_n),
103+
STMMAC_STAT(irq_rx_path_in_lpi_mode_n),
104+
STMMAC_STAT(irq_rx_path_exit_lpi_mode_n),
105+
STMMAC_STAT(phy_eee_wakeup_error_n),
96106
};
97107
#define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats)
98108

@@ -366,6 +376,11 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
366376
(*(u32 *)p);
367377
}
368378
}
379+
if (priv->eee_enabled) {
380+
int val = phy_get_eee_err(priv->phydev);
381+
if (val)
382+
priv->xstats.phy_eee_wakeup_error_n = val;
383+
}
369384
}
370385
for (i = 0; i < STMMAC_STATS_LEN; i++) {
371386
char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset;
@@ -464,6 +479,46 @@ static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
464479
return 0;
465480
}
466481

482+
static int stmmac_ethtool_op_get_eee(struct net_device *dev,
483+
struct ethtool_eee *edata)
484+
{
485+
struct stmmac_priv *priv = netdev_priv(dev);
486+
487+
if (!priv->dma_cap.eee)
488+
return -EOPNOTSUPP;
489+
490+
edata->eee_enabled = priv->eee_enabled;
491+
edata->eee_active = priv->eee_active;
492+
edata->tx_lpi_timer = priv->tx_lpi_timer;
493+
494+
return phy_ethtool_get_eee(priv->phydev, edata);
495+
}
496+
497+
static int stmmac_ethtool_op_set_eee(struct net_device *dev,
498+
struct ethtool_eee *edata)
499+
{
500+
struct stmmac_priv *priv = netdev_priv(dev);
501+
502+
priv->eee_enabled = edata->eee_enabled;
503+
504+
if (!priv->eee_enabled)
505+
stmmac_disable_eee_mode(priv);
506+
else {
507+
/* We are asking for enabling the EEE but it is safe
508+
* to verify all by invoking the eee_init function.
509+
* In case of failure it will return an error.
510+
*/
511+
priv->eee_enabled = stmmac_eee_init(priv);
512+
if (!priv->eee_enabled)
513+
return -EOPNOTSUPP;
514+
515+
/* Do not change tx_lpi_timer in case of failure */
516+
priv->tx_lpi_timer = edata->tx_lpi_timer;
517+
}
518+
519+
return phy_ethtool_set_eee(priv->phydev, edata);
520+
}
521+
467522
static const struct ethtool_ops stmmac_ethtool_ops = {
468523
.begin = stmmac_check_if_running,
469524
.get_drvinfo = stmmac_ethtool_getdrvinfo,
@@ -480,6 +535,8 @@ static const struct ethtool_ops stmmac_ethtool_ops = {
480535
.get_strings = stmmac_get_strings,
481536
.get_wol = stmmac_get_wol,
482537
.set_wol = stmmac_set_wol,
538+
.get_eee = stmmac_ethtool_op_get_eee,
539+
.set_eee = stmmac_ethtool_op_set_eee,
483540
.get_sset_count = stmmac_get_sset_count,
484541
.get_ts_info = ethtool_op_get_ts_info,
485542
};

0 commit comments

Comments
 (0)