Commit 6d81f451 authored by Russell King's avatar Russell King Committed by David S. Miller

net: mvneta: add EEE support

Add support for EEE to mvneta.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4932a918
...@@ -244,6 +244,12 @@ ...@@ -244,6 +244,12 @@
#define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2)) #define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2))
#define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff #define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff
#define MVNETA_LPI_CTRL_0 0x2cc0
#define MVNETA_LPI_CTRL_1 0x2cc4
#define MVNETA_LPI_REQUEST_ENABLE BIT(0)
#define MVNETA_LPI_CTRL_2 0x2cc8
#define MVNETA_LPI_STATUS 0x2ccc
#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff #define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff
/* Descriptor ring Macros */ /* Descriptor ring Macros */
...@@ -320,6 +326,11 @@ ...@@ -320,6 +326,11 @@
#define MVNETA_RX_GET_BM_POOL_ID(rxd) \ #define MVNETA_RX_GET_BM_POOL_ID(rxd) \
(((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT) (((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT)
enum {
ETHTOOL_STAT_EEE_WAKEUP,
ETHTOOL_MAX_STATS,
};
struct mvneta_statistic { struct mvneta_statistic {
unsigned short offset; unsigned short offset;
unsigned short type; unsigned short type;
...@@ -328,6 +339,7 @@ struct mvneta_statistic { ...@@ -328,6 +339,7 @@ struct mvneta_statistic {
#define T_REG_32 32 #define T_REG_32 32
#define T_REG_64 64 #define T_REG_64 64
#define T_SW 1
static const struct mvneta_statistic mvneta_statistics[] = { static const struct mvneta_statistic mvneta_statistics[] = {
{ 0x3000, T_REG_64, "good_octets_received", }, { 0x3000, T_REG_64, "good_octets_received", },
...@@ -362,6 +374,7 @@ static const struct mvneta_statistic mvneta_statistics[] = { ...@@ -362,6 +374,7 @@ static const struct mvneta_statistic mvneta_statistics[] = {
{ 0x304c, T_REG_32, "broadcast_frames_sent", }, { 0x304c, T_REG_32, "broadcast_frames_sent", },
{ 0x3054, T_REG_32, "fc_sent", }, { 0x3054, T_REG_32, "fc_sent", },
{ 0x300c, T_REG_32, "internal_mac_transmit_err", }, { 0x300c, T_REG_32, "internal_mac_transmit_err", },
{ ETHTOOL_STAT_EEE_WAKEUP, T_SW, "eee_wakeup_errors", },
}; };
struct mvneta_pcpu_stats { struct mvneta_pcpu_stats {
...@@ -424,6 +437,10 @@ struct mvneta_port { ...@@ -424,6 +437,10 @@ struct mvneta_port {
struct mvneta_bm_pool *pool_short; struct mvneta_bm_pool *pool_short;
int bm_win_id; int bm_win_id;
bool eee_enabled;
bool eee_active;
bool tx_lpi_enabled;
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
u32 indir[MVNETA_RSS_LU_TABLE_SIZE]; u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
...@@ -3369,6 +3386,18 @@ static void mvneta_mac_config(struct net_device *ndev, unsigned int mode, ...@@ -3369,6 +3386,18 @@ static void mvneta_mac_config(struct net_device *ndev, unsigned int mode,
} }
} }
static void mvneta_set_eee(struct mvneta_port *pp, bool enable)
{
u32 lpi_ctl1;
lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
if (enable)
lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE;
else
lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE;
mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1);
}
static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode) static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode)
{ {
struct mvneta_port *pp = netdev_priv(ndev); struct mvneta_port *pp = netdev_priv(ndev);
...@@ -3382,6 +3411,9 @@ static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode) ...@@ -3382,6 +3411,9 @@ static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode)
val |= MVNETA_GMAC_FORCE_LINK_DOWN; val |= MVNETA_GMAC_FORCE_LINK_DOWN;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
} }
pp->eee_active = false;
mvneta_set_eee(pp, false);
} }
static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode, static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode,
...@@ -3398,6 +3430,11 @@ static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode, ...@@ -3398,6 +3430,11 @@ static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode,
} }
mvneta_port_up(pp); mvneta_port_up(pp);
if (phy && pp->eee_enabled) {
pp->eee_active = phy_init_eee(phy, 0) >= 0;
mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled);
}
} }
static const struct phylink_mac_ops mvneta_phylink_ops = { static const struct phylink_mac_ops mvneta_phylink_ops = {
...@@ -3859,26 +3896,35 @@ static void mvneta_ethtool_update_stats(struct mvneta_port *pp) ...@@ -3859,26 +3896,35 @@ static void mvneta_ethtool_update_stats(struct mvneta_port *pp)
{ {
const struct mvneta_statistic *s; const struct mvneta_statistic *s;
void __iomem *base = pp->base; void __iomem *base = pp->base;
u32 high, low, val; u32 high, low;
u64 val64; u64 val;
int i; int i;
for (i = 0, s = mvneta_statistics; for (i = 0, s = mvneta_statistics;
s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics); s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics);
s++, i++) { s++, i++) {
val = 0;
switch (s->type) { switch (s->type) {
case T_REG_32: case T_REG_32:
val = readl_relaxed(base + s->offset); val = readl_relaxed(base + s->offset);
pp->ethtool_stats[i] += val;
break; break;
case T_REG_64: case T_REG_64:
/* Docs say to read low 32-bit then high */ /* Docs say to read low 32-bit then high */
low = readl_relaxed(base + s->offset); low = readl_relaxed(base + s->offset);
high = readl_relaxed(base + s->offset + 4); high = readl_relaxed(base + s->offset + 4);
val64 = (u64)high << 32 | low; val = (u64)high << 32 | low;
pp->ethtool_stats[i] += val64; break;
case T_SW:
switch (s->offset) {
case ETHTOOL_STAT_EEE_WAKEUP:
val = phylink_get_eee_err(pp->phylink);
break;
}
break; break;
} }
pp->ethtool_stats[i] += val;
} }
} }
...@@ -4031,6 +4077,47 @@ static int mvneta_ethtool_set_wol(struct net_device *dev, ...@@ -4031,6 +4077,47 @@ static int mvneta_ethtool_set_wol(struct net_device *dev,
return ret; return ret;
} }
static int mvneta_ethtool_get_eee(struct net_device *dev,
struct ethtool_eee *eee)
{
struct mvneta_port *pp = netdev_priv(dev);
u32 lpi_ctl0;
lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
eee->eee_enabled = pp->eee_enabled;
eee->eee_active = pp->eee_active;
eee->tx_lpi_enabled = pp->tx_lpi_enabled;
eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale;
return phylink_ethtool_get_eee(pp->phylink, eee);
}
static int mvneta_ethtool_set_eee(struct net_device *dev,
struct ethtool_eee *eee)
{
struct mvneta_port *pp = netdev_priv(dev);
u32 lpi_ctl0;
/* The Armada 37x documents do not give limits for this other than
* it being an 8-bit register. */
if (eee->tx_lpi_enabled &&
(eee->tx_lpi_timer < 0 || eee->tx_lpi_timer > 255))
return -EINVAL;
lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
lpi_ctl0 &= ~(0xff << 8);
lpi_ctl0 |= eee->tx_lpi_timer << 8;
mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0);
pp->eee_enabled = eee->eee_enabled;
pp->tx_lpi_enabled = eee->tx_lpi_enabled;
mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled);
return phylink_ethtool_set_eee(pp->phylink, eee);
}
static const struct net_device_ops mvneta_netdev_ops = { static const struct net_device_ops mvneta_netdev_ops = {
.ndo_open = mvneta_open, .ndo_open = mvneta_open,
.ndo_stop = mvneta_stop, .ndo_stop = mvneta_stop,
...@@ -4064,6 +4151,8 @@ static const struct ethtool_ops mvneta_eth_tool_ops = { ...@@ -4064,6 +4151,8 @@ static const struct ethtool_ops mvneta_eth_tool_ops = {
.set_link_ksettings = mvneta_ethtool_set_link_ksettings, .set_link_ksettings = mvneta_ethtool_set_link_ksettings,
.get_wol = mvneta_ethtool_get_wol, .get_wol = mvneta_ethtool_get_wol,
.set_wol = mvneta_ethtool_set_wol, .set_wol = mvneta_ethtool_set_wol,
.get_eee = mvneta_ethtool_get_eee,
.set_eee = mvneta_ethtool_set_eee,
}; };
/* Initialize hw */ /* Initialize hw */
......
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