Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d765955d authored by Giuseppe CAVALLARO's avatar Giuseppe CAVALLARO Committed by David S. Miller
Browse files

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: default avatarGiuseppe Cavallaro <peppe.cavallaro@st.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0ec2ccd0
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -95,6 +95,16 @@ struct stmmac_extra_stats {
	unsigned long poll_n;
	unsigned long sched_timer_n;
	unsigned long normal_irq_n;
	unsigned long mmc_tx_irq_n;
	unsigned long mmc_rx_irq_n;
	unsigned long mmc_rx_csum_offload_irq_n;
	/* EEE */
	unsigned long irq_receive_pmt_irq_n;
	unsigned long irq_tx_path_in_lpi_mode_n;
	unsigned long irq_tx_path_exit_lpi_mode_n;
	unsigned long irq_rx_path_in_lpi_mode_n;
	unsigned long irq_rx_path_exit_lpi_mode_n;
	unsigned long phy_eee_wakeup_error_n;
};

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

enum core_specific_irq_mask {
	core_mmc_tx_irq = 1,
	core_mmc_rx_irq = 2,
	core_mmc_rx_csum_offload_irq = 4,
	core_irq_receive_pmt_irq = 8,
	core_irq_tx_path_in_lpi_mode = 16,
	core_irq_tx_path_exit_lpi_mode = 32,
	core_irq_rx_path_in_lpi_mode = 64,
	core_irq_rx_path_exit_lpi_mode = 128,
};

/* DMA HW capabilities */
struct dma_features {
	unsigned int mbps_10_100;
@@ -208,6 +229,10 @@ struct dma_features {
#define MAC_ENABLE_TX		0x00000008	/* Transmitter Enable */
#define MAC_RNABLE_RX		0x00000004	/* Receiver Enable */

/* Default LPI timers */
#define STMMAC_DEFAULT_LIT_LS_TIMER	0x3E8
#define STMMAC_DEFAULT_TWT_LS_TIMER	0x0

struct stmmac_desc_ops {
	/* DMA RX descriptor ring initialization */
	void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size,
@@ -278,7 +303,7 @@ struct stmmac_ops {
	/* Dump MAC registers */
	void (*dump_regs) (void __iomem *ioaddr);
	/* Handle extra events on specific interrupts hw dependent */
	void (*host_irq_status) (void __iomem *ioaddr);
	int (*host_irq_status) (void __iomem *ioaddr);
	/* Multicast filter setting */
	void (*set_filter) (struct net_device *dev, int id);
	/* Flow control setting */
@@ -291,6 +316,10 @@ struct stmmac_ops {
			       unsigned int reg_n);
	void (*get_umac_addr) (void __iomem *ioaddr, unsigned char *addr,
			       unsigned int reg_n);
	void (*set_eee_mode) (void __iomem *ioaddr);
	void (*reset_eee_mode) (void __iomem *ioaddr);
	void (*set_eee_timer) (void __iomem *ioaddr, int ls, int tw);
	void (*set_eee_pls) (void __iomem *ioaddr, int link);
};

struct mac_link {
+20 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@

#define GMAC_INT_STATUS		0x00000038	/* interrupt status register */
enum dwmac1000_irq_status {
	lpiis_irq = 0x400,
	time_stamp_irq = 0x0200,
	mmc_rx_csum_offload_irq = 0x0080,
	mmc_tx_irq = 0x0040,
@@ -60,6 +61,25 @@ enum power_event {
	power_down = 0x00000001,
};

/* Energy Efficient Ethernet (EEE)
 *
 * LPI status, timer and control register offset
 */
#define LPI_CTRL_STATUS	0x0030
#define LPI_TIMER_CTRL	0x0034

/* LPI control and status defines */
#define LPI_CTRL_STATUS_LPITXA	0x00080000	/* Enable LPI TX Automate */
#define LPI_CTRL_STATUS_PLSEN	0x00040000	/* Enable PHY Link Status */
#define LPI_CTRL_STATUS_PLS	0x00020000	/* PHY Link Status */
#define LPI_CTRL_STATUS_LPIEN	0x00010000	/* LPI Enable */
#define LPI_CTRL_STATUS_RLPIST	0x00000200	/* Receive LPI state */
#define LPI_CTRL_STATUS_TLPIST	0x00000100	/* Transmit LPI state */
#define LPI_CTRL_STATUS_RLPIEX	0x00000008	/* Receive LPI Exit */
#define LPI_CTRL_STATUS_RLPIEN	0x00000004	/* Receive LPI Entry */
#define LPI_CTRL_STATUS_TLPIEX	0x00000002	/* Transmit LPI Exit */
#define LPI_CTRL_STATUS_TLPIEN	0x00000001	/* Transmit LPI Entry */

/* GMAC HW ADDR regs */
#define GMAC_ADDR_HIGH(reg)	(((reg > 15) ? 0x00000800 : 0x00000040) + \
				(reg * 8))
+93 −8
Original line number Diff line number Diff line
@@ -194,26 +194,107 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode)
}


static void dwmac1000_irq_status(void __iomem *ioaddr)
static int dwmac1000_irq_status(void __iomem *ioaddr)
{
	u32 intr_status = readl(ioaddr + GMAC_INT_STATUS);
	int status = 0;

	/* Not used events (e.g. MMC interrupts) are not handled. */
	if ((intr_status & mmc_tx_irq))
		CHIP_DBG(KERN_DEBUG "GMAC: MMC tx interrupt: 0x%08x\n",
	if ((intr_status & mmc_tx_irq)) {
		CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n",
		    readl(ioaddr + GMAC_MMC_TX_INTR));
	if (unlikely(intr_status & mmc_rx_irq))
		CHIP_DBG(KERN_DEBUG "GMAC: MMC rx interrupt: 0x%08x\n",
		status |= core_mmc_tx_irq;
	}
	if (unlikely(intr_status & mmc_rx_irq)) {
		CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n",
		    readl(ioaddr + GMAC_MMC_RX_INTR));
	if (unlikely(intr_status & mmc_rx_csum_offload_irq))
		CHIP_DBG(KERN_DEBUG "GMAC: MMC rx csum offload: 0x%08x\n",
		status |= core_mmc_rx_irq;
	}
	if (unlikely(intr_status & mmc_rx_csum_offload_irq)) {
		CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n",
		    readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD));
		status |= core_mmc_rx_csum_offload_irq;
	}
	if (unlikely(intr_status & pmt_irq)) {
		CHIP_DBG(KERN_DEBUG "GMAC: received Magic frame\n");
		CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n");
		/* clear the PMT bits 5 and 6 by reading the PMT
		 * status register. */
		readl(ioaddr + GMAC_PMT);
		status |= core_irq_receive_pmt_irq;
	}
	/* MAC trx/rx EEE LPI entry/exit interrupts */
	if (intr_status & lpiis_irq) {
		/* Clean LPI interrupt by reading the Reg 12 */
		u32 lpi_status = readl(ioaddr + LPI_CTRL_STATUS);

		if (lpi_status & LPI_CTRL_STATUS_TLPIEN) {
			CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n");
			status |= core_irq_tx_path_in_lpi_mode;
		}
		if (lpi_status & LPI_CTRL_STATUS_TLPIEX) {
			CHIP_DBG(KERN_INFO "GMAC TX exit from LPI\n");
			status |= core_irq_tx_path_exit_lpi_mode;
		}
		if (lpi_status & LPI_CTRL_STATUS_RLPIEN) {
			CHIP_DBG(KERN_INFO "GMAC RX entered in LPI\n");
			status |= core_irq_rx_path_in_lpi_mode;
		}
		if (lpi_status & LPI_CTRL_STATUS_RLPIEX) {
			CHIP_DBG(KERN_INFO "GMAC RX exit from LPI\n");
			status |= core_irq_rx_path_exit_lpi_mode;
		}
	}

	return status;
}

static void  dwmac1000_set_eee_mode(void __iomem *ioaddr)
{
	u32 value;

	/* Enable the link status receive on RGMII, SGMII ore SMII
	 * receive path and instruct the transmit to enter in LPI
	 * state. */
	value = readl(ioaddr + LPI_CTRL_STATUS);
	value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
	writel(value, ioaddr + LPI_CTRL_STATUS);
}

static void  dwmac1000_reset_eee_mode(void __iomem *ioaddr)
{
	u32 value;

	value = readl(ioaddr + LPI_CTRL_STATUS);
	value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA);
	writel(value, ioaddr + LPI_CTRL_STATUS);
}

static void  dwmac1000_set_eee_pls(void __iomem *ioaddr, int link)
{
	u32 value;

	value = readl(ioaddr + LPI_CTRL_STATUS);

	if (link)
		value |= LPI_CTRL_STATUS_PLS;
	else
		value &= ~LPI_CTRL_STATUS_PLS;

	writel(value, ioaddr + LPI_CTRL_STATUS);
}

static void  dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw)
{
	int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16);

	/* Program the timers in the LPI timer control register:
	 * LS: minimum time (ms) for which the link
	 *  status from PHY should be ok before transmitting
	 *  the LPI pattern.
	 * TW: minimum time (us) for which the core waits
	 *  after it has stopped transmitting the LPI pattern.
	 */
	writel(value, ioaddr + LPI_TIMER_CTRL);
}

static const struct stmmac_ops dwmac1000_ops = {
@@ -226,6 +307,10 @@ static const struct stmmac_ops dwmac1000_ops = {
	.pmt = dwmac1000_pmt,
	.set_umac_addr = dwmac1000_set_umac_addr,
	.get_umac_addr = dwmac1000_get_umac_addr,
	.set_eee_mode =  dwmac1000_set_eee_mode,
	.reset_eee_mode =  dwmac1000_reset_eee_mode,
	.set_eee_timer =  dwmac1000_set_eee_timer,
	.set_eee_pls =  dwmac1000_set_eee_pls,
};

struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr)
+2 −2
Original line number Diff line number Diff line
@@ -72,9 +72,9 @@ static int dwmac100_rx_ipc_enable(void __iomem *ioaddr)
	return 0;
}

static void dwmac100_irq_status(void __iomem *ioaddr)
static int dwmac100_irq_status(void __iomem *ioaddr)
{
	return;
	return 0;
}

static void dwmac100_set_umac_addr(void __iomem *ioaddr, unsigned char *addr,
+1 −0
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@
#define DMA_INTR_DEFAULT_MASK	(DMA_INTR_NORMAL | DMA_INTR_ABNORMAL)

/* DMA Status register defines */
#define DMA_STATUS_GLPII	0x40000000	/* GMAC LPI interrupt */
#define DMA_STATUS_GPI		0x10000000	/* PMT interrupt */
#define DMA_STATUS_GMI		0x08000000	/* MMC interrupt */
#define DMA_STATUS_GLI		0x04000000	/* GMAC Line interface int */
Loading