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

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

stmmac: initial support to manage pcs modes



This patch adds the minimal support to manage the PCS
modes (RGMII/SGMII) and restart the ANE.
Both TBI and RTBI are not yet supported.

Thanks to Byungho that wrote some part of this code
and tested SGMII too.

The only thing to be fixed is the get/set pause in
ethtool.

Signed-off-by: default avatarByungho An <bh74.an@samsung.com>
Signed-off-by: default avatarGiuseppe Cavallaro <peppe.cavallaro@st.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0982a0f6
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -144,6 +144,9 @@ struct stmmac_extra_stats {
	unsigned long irq_pcs_ane_n;
	unsigned long irq_pcs_link_n;
	unsigned long irq_rgmii_n;
	unsigned long pcs_link;
	unsigned long pcs_duplex;
	unsigned long pcs_speed;
};

/* CSR Frequency Access Defines*/
@@ -165,6 +168,12 @@ struct stmmac_extra_stats {
#define FLOW_TX		2
#define FLOW_AUTO	(FLOW_TX | FLOW_RX)

/* PCS defines */
#define STMMAC_PCS_RGMII	(1 << 0)
#define STMMAC_PCS_SGMII	(1 << 1)
#define STMMAC_PCS_TBI		(1 << 2)
#define STMMAC_PCS_RTBI		(1 << 3)

#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */

/* DAM HW feature register fields */
@@ -230,6 +239,16 @@ enum dma_irq_status {
#define	CORE_PCS_LINK_STATUS		(1 << 6)
#define	CORE_RGMII_IRQ			(1 << 7)

struct rgmii_adv {
	unsigned int pause;
	unsigned int duplex;
	unsigned int lp_pause;
	unsigned int lp_duplex;
};

#define STMMAC_PCS_PAUSE	1
#define STMMAC_PCS_ASYM_PAUSE	2

/* DMA HW capabilities */
struct dma_features {
	unsigned int mbps_10_100;
@@ -375,6 +394,8 @@ struct stmmac_ops {
	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);
	void (*ctrl_ane) (void __iomem *ioaddr, bool restart);
	void (*get_adv) (void __iomem *ioaddr, struct rgmii_adv *adv);
};

struct mac_link {
+34 −0
Original line number Diff line number Diff line
@@ -98,6 +98,38 @@ enum power_event {
#define GMAC_TBI	0x000000d4	/* TBI extend status */
#define GMAC_S_R_GMII	0x000000d8	/* SGMII RGMII status */

/* AN Configuration defines */
#define GMAC_AN_CTRL_RAN	0x00000200 /* Restart Auto-Negotiation */
#define GMAC_AN_CTRL_ANE	0x00001000 /* Auto-Negotiation Enable */
#define GMAC_AN_CTRL_ELE	0x00004000 /* External Loopback Enable */
#define GMAC_AN_CTRL_ECD	0x00010000 /* Enable Comma Detect */
#define GMAC_AN_CTRL_LR	0x00020000 /* Lock to Reference */
#define GMAC_AN_CTRL_SGMRAL	0x00040000 /* SGMII RAL Control */

/* AN Status defines */
#define GMAC_AN_STATUS_LS	0x00000004 /* Link Status 0:down 1:up */
#define GMAC_AN_STATUS_ANA	0x00000008 /* Auto-Negotiation Ability */
#define GMAC_AN_STATUS_ANC	0x00000020 /* Auto-Negotiation Complete */
#define GMAC_AN_STATUS_ES	0x00000100 /* Extended Status */

/* Register 54 (SGMII/RGMII status register) */
#define GMAC_S_R_GMII_LINK		0x8
#define GMAC_S_R_GMII_SPEED		0x5
#define GMAC_S_R_GMII_SPEED_SHIFT	0x1
#define GMAC_S_R_GMII_MODE		0x1
#define GMAC_S_R_GMII_SPEED_125		2
#define GMAC_S_R_GMII_SPEED_25		1

/* Common ADV and LPA defines */
#define GMAC_ANE_FD		(1 << 5)
#define GMAC_ANE_HD		(1 << 6)
#define GMAC_ANE_PSE		(3 << 7)
#define GMAC_ANE_PSE_SHIFT	7

 /* GMAC Configuration defines */
#define GMAC_CONTROL_TC	0x01000000 /* Transmit Conf. in RGMII/SGMII */
#define GMAC_CONTROL_WD	0x00800000 /* Disable Watchdog on receive */

/* GMAC Configuration defines */
#define GMAC_CONTROL_TC	0x01000000	/* Transmit Conf. in RGMII/SGMII */
#define GMAC_CONTROL_WD	0x00800000	/* Disable Watchdog on receive */
@@ -232,5 +264,7 @@ enum rtc_control {
#define GMAC_MMC_TX_INTR   0x108
#define GMAC_MMC_RX_CSUM_OFFLOAD   0x208



extern const struct stmmac_dma_ops dwmac1000_dma_ops;
#endif /* __DWMAC1000_H__ */
+61 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@

#include <linux/crc32.h>
#include <linux/slab.h>
#include <linux/ethtool.h>
#include <asm/io.h>
#include "dwmac1000.h"

@@ -193,7 +194,6 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode)
	writel(pmt, ioaddr + GMAC_PMT);
}


static int dwmac1000_irq_status(void __iomem *ioaddr,
				struct stmmac_extra_stats *x)
{
@@ -252,9 +252,30 @@ static int dwmac1000_irq_status(void __iomem *ioaddr,
		x->irq_pcs_ane_n++;
	}
	if (intr_status & rgmii_irq) {
		CHIP_DBG(KERN_INFO "GMAC RGMII IRQ status\n");
		readl(ioaddr + GMAC_S_R_GMII);
		u32 status  = readl(ioaddr + GMAC_S_R_GMII);
		CHIP_DBG(KERN_INFO "GMAC RGMII/SGMII interrupt\n");
		x->irq_rgmii_n++;

		/* Save and dump the link status. */
		if (status & GMAC_S_R_GMII_LINK) {
			int speed_value = (status & GMAC_S_R_GMII_SPEED) >>
					  GMAC_S_R_GMII_SPEED_SHIFT;
			x->pcs_duplex = (status & GMAC_S_R_GMII_MODE);

			if (speed_value == GMAC_S_R_GMII_SPEED_125)
				x->pcs_speed = SPEED_1000;
			else if (speed_value == GMAC_S_R_GMII_SPEED_25)
				x->pcs_speed = SPEED_100;
			else
				x->pcs_speed = SPEED_10;

			x->pcs_link = 1;
			pr_debug("Link is Up - %d/%s\n", (int) x->pcs_speed,
				 x->pcs_duplex ? "Full" : "Half");
		} else {
			x->pcs_link = 0;
			pr_debug("Link is Down\n");
		}
	}

	return ret;
@@ -309,6 +330,41 @@ static void dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw)
	writel(value, ioaddr + LPI_TIMER_CTRL);
}

static void dwmac1000_ctrl_ane(void __iomem *ioaddr, bool restart)
{
	u32 value;

	value = readl(ioaddr + GMAC_AN_CTRL);
	/* auto negotiation enable and External Loopback enable */
	value = GMAC_AN_CTRL_ANE | GMAC_AN_CTRL_ELE;

	if (restart)
		value |= GMAC_AN_CTRL_RAN;

	writel(value, ioaddr + GMAC_AN_CTRL);
}

static void dwmac1000_get_adv(void __iomem *ioaddr, struct rgmii_adv *adv)
{
	u32 value = readl(ioaddr + GMAC_ANE_ADV);

	if (value & GMAC_ANE_FD)
		adv->duplex = DUPLEX_FULL;
	if (value & GMAC_ANE_HD)
		adv->duplex |= DUPLEX_HALF;

	adv->pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT;

	value = readl(ioaddr + GMAC_ANE_LPA);

	if (value & GMAC_ANE_FD)
		adv->lp_duplex = DUPLEX_FULL;
	if (value & GMAC_ANE_HD)
		adv->lp_duplex = DUPLEX_HALF;

	adv->lp_pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT;
}

static const struct stmmac_ops dwmac1000_ops = {
	.core_init = dwmac1000_core_init,
	.rx_ipc = dwmac1000_rx_ipc_enable,
@@ -323,6 +379,8 @@ static const struct stmmac_ops dwmac1000_ops = {
	.reset_eee_mode =  dwmac1000_reset_eee_mode,
	.set_eee_timer =  dwmac1000_set_eee_timer,
	.set_eee_pls =  dwmac1000_set_eee_pls,
	.ctrl_ane = dwmac1000_ctrl_ane,
	.get_adv = dwmac1000_get_adv,
};

struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr)
+1 −0
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ struct stmmac_priv {
	u32 rx_riwt;
	unsigned int mode;
	int extend_desc;
	int pcs;
};

extern int phyaddr;
+94 −0
Original line number Diff line number Diff line
@@ -246,6 +246,70 @@ static int stmmac_ethtool_getsettings(struct net_device *dev,
	struct stmmac_priv *priv = netdev_priv(dev);
	struct phy_device *phy = priv->phydev;
	int rc;

	if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) {
		struct rgmii_adv adv;

		if (!priv->xstats.pcs_link) {
			ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
			cmd->duplex = DUPLEX_UNKNOWN;
			return 0;
		}
		cmd->duplex = priv->xstats.pcs_duplex;

		ethtool_cmd_speed_set(cmd, priv->xstats.pcs_speed);

		/* Get and convert ADV/LP_ADV from the HW AN registers */
		if (priv->hw->mac->get_adv)
			priv->hw->mac->get_adv(priv->ioaddr, &adv);
		else
			return -EOPNOTSUPP;	/* should never happen indeed */

		/* Encoding of PSE bits is defined in 802.3z, 37.2.1.4 */

		if (adv.pause & STMMAC_PCS_PAUSE)
			cmd->advertising |= ADVERTISED_Pause;
		if (adv.pause & STMMAC_PCS_ASYM_PAUSE)
			cmd->advertising |= ADVERTISED_Asym_Pause;
		if (adv.lp_pause & STMMAC_PCS_PAUSE)
			cmd->lp_advertising |= ADVERTISED_Pause;
		if (adv.lp_pause & STMMAC_PCS_ASYM_PAUSE)
			cmd->lp_advertising |= ADVERTISED_Asym_Pause;

		/* Reg49[3] always set because ANE is always supported */
		cmd->autoneg = ADVERTISED_Autoneg;
		cmd->supported |= SUPPORTED_Autoneg;
		cmd->advertising |= ADVERTISED_Autoneg;
		cmd->lp_advertising |= ADVERTISED_Autoneg;

		if (adv.duplex) {
			cmd->supported |= (SUPPORTED_1000baseT_Full |
					   SUPPORTED_100baseT_Full |
					   SUPPORTED_10baseT_Full);
			cmd->advertising |= (ADVERTISED_1000baseT_Full |
					     ADVERTISED_100baseT_Full |
					     ADVERTISED_10baseT_Full);
		} else {
			cmd->supported |= (SUPPORTED_1000baseT_Half |
					   SUPPORTED_100baseT_Half |
					   SUPPORTED_10baseT_Half);
			cmd->advertising |= (ADVERTISED_1000baseT_Half |
					     ADVERTISED_100baseT_Half |
					     ADVERTISED_10baseT_Half);
		}
		if (adv.lp_duplex)
			cmd->lp_advertising |= (ADVERTISED_1000baseT_Full |
						ADVERTISED_100baseT_Full |
						ADVERTISED_10baseT_Full);
		else
			cmd->lp_advertising |= (ADVERTISED_1000baseT_Half |
						ADVERTISED_100baseT_Half |
						ADVERTISED_10baseT_Half);
		cmd->port = PORT_OTHER;

		return 0;
	}

	if (phy == NULL) {
		pr_err("%s: %s: PHY is not registered\n",
		       __func__, dev->name);
@@ -270,6 +334,30 @@ static int stmmac_ethtool_setsettings(struct net_device *dev,
	struct phy_device *phy = priv->phydev;
	int rc;

	if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) {
		u32 mask = ADVERTISED_Autoneg | ADVERTISED_Pause;

		/* Only support ANE */
		if (cmd->autoneg != AUTONEG_ENABLE)
			return -EINVAL;

		if (cmd->autoneg == AUTONEG_ENABLE) {
			mask &= (ADVERTISED_1000baseT_Half |
			ADVERTISED_1000baseT_Full |
			ADVERTISED_100baseT_Half |
			ADVERTISED_100baseT_Full |
			ADVERTISED_10baseT_Half |
			ADVERTISED_10baseT_Full);

			spin_lock(&priv->lock);
			if (priv->hw->mac->ctrl_ane)
				priv->hw->mac->ctrl_ane(priv->ioaddr, 1);
			spin_unlock(&priv->lock);
		}

		return 0;
	}

	spin_lock(&priv->lock);
	rc = phy_ethtool_sset(phy, cmd);
	spin_unlock(&priv->lock);
@@ -339,6 +427,9 @@ stmmac_get_pauseparam(struct net_device *netdev,
{
	struct stmmac_priv *priv = netdev_priv(netdev);

	if (priv->pcs)	/* FIXME */
		return;

	spin_lock(&priv->lock);

	pause->rx_pause = 0;
@@ -362,6 +453,9 @@ stmmac_set_pauseparam(struct net_device *netdev,
	int new_pause = FLOW_OFF;
	int ret = 0;

	if (priv->pcs)	/* FIXME */
		return -EOPNOTSUPP;

	spin_lock(&priv->lock);

	if (pause->rx_pause)
Loading