Commit 1d641bd3 authored by David S. Miller's avatar David S. Miller

Merge branch 'mvneta-phylink'

Russell King says:

====================
Convert mvneta to phylink

This series converts mvneta to use phylink, which is necessary to
support the SFP cages on SolidRun's Clearfog platform.  This series just
converts mvneta without adding the DT parts - having discussed with
Andrew, we believe we're too close to the merge window to submit that
patch.

I've split the "net: mvneta: convert to phylink" patch up to make it
easier to review, and in doing so, spotted some minor corner cases that
needed to be fixed along the way.

This series depends on the previously merged phylink patches in netdev,
along with the recently reviewed 7 patch series "Resolve races in phy
accessors" without which, the race described in patch 5 of that series
is very evident when triggering a dummy hibernate cycle.

This series also illustrates how to convert mvpp2 to phylink.

mvneta is the only user of the fixed_phy_update_state() API, and this
becomes redundant with the conversion.

It would be good to get this series not only reviewed, but also
independently tested to ensure that I haven't missed anything - I only
have the Clearfog platform to test on, and that doesn't support all the
different interface modes that mvneta supports.

A particularly interesting side effect of this series is that DSA
switches no longer need the "CPU" port and DSA facing MAC ethernet
instance to be marked as a fixed link anymore with mvneta - we can use
1000BaseX mode, and the DSA to CPU link will use the 802.3z negotiation
to determine the link properties without needing the link parameters to
be explicitly stated in DT - that is a subject of a future patch.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d033a236 d2b97793
......@@ -60,7 +60,7 @@ config MVNETA
depends on ARCH_MVEBU || COMPILE_TEST
depends on HAS_DMA
select MVMDIO
select FIXED_PHY
select PHYLINK
---help---
This driver supports the network interface units in the
Marvell ARMADA XP, ARMADA 370, ARMADA 38x and
......
......@@ -28,7 +28,7 @@
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/phylink.h>
#include <linux/platform_device.h>
#include <linux/skbuff.h>
#include <net/hwbm.h>
......@@ -189,6 +189,7 @@
#define MVNETA_GMAC_CTRL_0 0x2c00
#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2
#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc
#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1)
#define MVNETA_GMAC0_PORT_ENABLE BIT(0)
#define MVNETA_GMAC_CTRL_2 0x2c08
#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0)
......@@ -204,13 +205,19 @@
#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5)
#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6)
#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7)
#define MVNETA_GMAC_AN_COMPLETE BIT(11)
#define MVNETA_GMAC_SYNC_OK BIT(14)
#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c
#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0)
#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1)
#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2)
#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3)
#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4)
#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5)
#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6)
#define MVNETA_GMAC_AN_SPEED_EN BIT(7)
#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8)
#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9)
#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11)
#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12)
#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13)
......@@ -237,6 +244,12 @@
#define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2))
#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
/* Descriptor ring Macros */
......@@ -313,6 +326,11 @@
#define MVNETA_RX_GET_BM_POOL_ID(rxd) \
(((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT)
enum {
ETHTOOL_STAT_EEE_WAKEUP,
ETHTOOL_MAX_STATS,
};
struct mvneta_statistic {
unsigned short offset;
unsigned short type;
......@@ -321,6 +339,7 @@ struct mvneta_statistic {
#define T_REG_32 32
#define T_REG_64 64
#define T_SW 1
static const struct mvneta_statistic mvneta_statistics[] = {
{ 0x3000, T_REG_64, "good_octets_received", },
......@@ -355,6 +374,7 @@ static const struct mvneta_statistic mvneta_statistics[] = {
{ 0x304c, T_REG_32, "broadcast_frames_sent", },
{ 0x3054, T_REG_32, "fc_sent", },
{ 0x300c, T_REG_32, "internal_mac_transmit_err", },
{ ETHTOOL_STAT_EEE_WAKEUP, T_SW, "eee_wakeup_errors", },
};
struct mvneta_pcpu_stats {
......@@ -407,20 +427,20 @@ struct mvneta_port {
u16 tx_ring_size;
u16 rx_ring_size;
struct mii_bus *mii_bus;
phy_interface_t phy_interface;
struct device_node *phy_node;
unsigned int link;
unsigned int duplex;
unsigned int speed;
struct device_node *dn;
unsigned int tx_csum_limit;
unsigned int use_inband_status:1;
struct phylink *phylink;
struct mvneta_bm *bm_priv;
struct mvneta_bm_pool *pool_long;
struct mvneta_bm_pool *pool_short;
int bm_win_id;
bool eee_enabled;
bool eee_active;
bool tx_lpi_enabled;
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
......@@ -1214,10 +1234,6 @@ static void mvneta_port_disable(struct mvneta_port *pp)
val &= ~MVNETA_GMAC0_PORT_ENABLE;
mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
pp->link = 0;
pp->duplex = -1;
pp->speed = 0;
udelay(200);
}
......@@ -1277,44 +1293,6 @@ static void mvneta_set_other_mcast_table(struct mvneta_port *pp, int queue)
mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val);
}
static void mvneta_set_autoneg(struct mvneta_port *pp, int enable)
{
u32 val;
if (enable) {
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
MVNETA_GMAC_FORCE_LINK_DOWN |
MVNETA_GMAC_AN_FLOW_CTRL_EN);
val |= MVNETA_GMAC_INBAND_AN_ENABLE |
MVNETA_GMAC_AN_SPEED_EN |
MVNETA_GMAC_AN_DUPLEX_EN;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
val |= MVNETA_GMAC2_INBAND_AN_ENABLE;
mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
} else {
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
MVNETA_GMAC_AN_SPEED_EN |
MVNETA_GMAC_AN_DUPLEX_EN);
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE;
mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
}
}
static void mvneta_percpu_unmask_interrupt(void *arg)
{
struct mvneta_port *pp = arg;
......@@ -1467,7 +1445,6 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
val &= ~MVNETA_PHY_POLLING_ENABLE;
mvreg_write(pp, MVNETA_UNIT_CONTROL, val);
mvneta_set_autoneg(pp, pp->use_inband_status);
mvneta_set_ucast_table(pp, -1);
mvneta_set_special_mcast_table(pp, -1);
mvneta_set_other_mcast_table(pp, -1);
......@@ -2692,26 +2669,11 @@ static irqreturn_t mvneta_percpu_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int mvneta_fixed_link_update(struct mvneta_port *pp,
struct phy_device *phy)
static void mvneta_link_change(struct mvneta_port *pp)
{
struct fixed_phy_status status;
struct fixed_phy_status changed = {};
u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
status.link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
if (gmac_stat & MVNETA_GMAC_SPEED_1000)
status.speed = SPEED_1000;
else if (gmac_stat & MVNETA_GMAC_SPEED_100)
status.speed = SPEED_100;
else
status.speed = SPEED_10;
status.duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
changed.link = 1;
changed.speed = 1;
changed.duplex = 1;
fixed_phy_update_state(phy, &status, &changed);
return 0;
phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP));
}
/* NAPI handler
......@@ -2727,7 +2689,6 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
u32 cause_rx_tx;
int rx_queue;
struct mvneta_port *pp = netdev_priv(napi->dev);
struct net_device *ndev = pp->dev;
struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
if (!netif_running(pp->dev)) {
......@@ -2741,12 +2702,10 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE);
mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
if (pp->use_inband_status && (cause_misc &
(MVNETA_CAUSE_PHY_STATUS_CHANGE |
MVNETA_CAUSE_LINK_CHANGE |
MVNETA_CAUSE_PSC_SYNC_CHANGE))) {
mvneta_fixed_link_update(pp, ndev->phydev);
}
if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE |
MVNETA_CAUSE_LINK_CHANGE))
mvneta_link_change(pp);
}
/* Release Tx descriptors */
......@@ -3060,7 +3019,6 @@ static int mvneta_setup_txqs(struct mvneta_port *pp)
static void mvneta_start_dev(struct mvneta_port *pp)
{
int cpu;
struct net_device *ndev = pp->dev;
mvneta_max_rx_size_set(pp, pp->pkt_size);
mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
......@@ -3085,19 +3043,17 @@ static void mvneta_start_dev(struct mvneta_port *pp)
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
MVNETA_CAUSE_PHY_STATUS_CHANGE |
MVNETA_CAUSE_LINK_CHANGE |
MVNETA_CAUSE_PSC_SYNC_CHANGE);
MVNETA_CAUSE_LINK_CHANGE);
phy_start(ndev->phydev);
phylink_start(pp->phylink);
netif_tx_start_all_queues(pp->dev);
}
static void mvneta_stop_dev(struct mvneta_port *pp)
{
unsigned int cpu;
struct net_device *ndev = pp->dev;
phy_stop(ndev->phydev);
phylink_stop(pp->phylink);
if (!pp->neta_armada3700) {
for_each_online_cpu(cpu) {
......@@ -3251,103 +3207,260 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
return 0;
}
static void mvneta_adjust_link(struct net_device *ndev)
static void mvneta_validate(struct net_device *ndev, unsigned long *supported,
struct phylink_link_state *state)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
/* We only support QSGMII, SGMII, 802.3z and RGMII modes */
if (state->interface != PHY_INTERFACE_MODE_NA &&
state->interface != PHY_INTERFACE_MODE_QSGMII &&
state->interface != PHY_INTERFACE_MODE_SGMII &&
!phy_interface_mode_is_8023z(state->interface) &&
!phy_interface_mode_is_rgmii(state->interface)) {
bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
return;
}
/* Allow all the expected bits */
phylink_set(mask, Autoneg);
phylink_set_port_modes(mask);
/* Asymmetric pause is unsupported */
phylink_set(mask, Pause);
/* Half-duplex at speeds higher than 100Mbit is unsupported */
phylink_set(mask, 1000baseT_Full);
phylink_set(mask, 1000baseX_Full);
if (!phy_interface_mode_is_8023z(state->interface)) {
/* 10M and 100M are only supported in non-802.3z mode */
phylink_set(mask, 10baseT_Half);
phylink_set(mask, 10baseT_Full);
phylink_set(mask, 100baseT_Half);
phylink_set(mask, 100baseT_Full);
}
bitmap_and(supported, supported, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_and(state->advertising, state->advertising, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
}
static int mvneta_mac_link_state(struct net_device *ndev,
struct phylink_link_state *state)
{
struct mvneta_port *pp = netdev_priv(ndev);
struct phy_device *phydev = ndev->phydev;
int status_change = 0;
u32 gmac_stat;
gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
if (phydev->link) {
if ((pp->speed != phydev->speed) ||
(pp->duplex != phydev->duplex)) {
u32 val;
if (gmac_stat & MVNETA_GMAC_SPEED_1000)
state->speed = SPEED_1000;
else if (gmac_stat & MVNETA_GMAC_SPEED_100)
state->speed = SPEED_100;
else
state->speed = SPEED_10;
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
MVNETA_GMAC_CONFIG_GMII_SPEED |
MVNETA_GMAC_CONFIG_FULL_DUPLEX);
state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE);
state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
state->pause = 0;
if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE)
state->pause |= MLO_PAUSE_RX;
if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE)
state->pause |= MLO_PAUSE_TX;
return 1;
}
if (phydev->duplex)
val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
static void mvneta_mac_an_restart(struct net_device *ndev)
{
struct mvneta_port *pp = netdev_priv(ndev);
u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
if (phydev->speed == SPEED_1000)
val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
else if (phydev->speed == SPEED_100)
val |= MVNETA_GMAC_CONFIG_MII_SPEED;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
gmac_an | MVNETA_GMAC_INBAND_RESTART_AN);
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN);
}
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
static void mvneta_mac_config(struct net_device *ndev, unsigned int mode,
const struct phylink_link_state *state)
{
struct mvneta_port *pp = netdev_priv(ndev);
u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X;
new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE |
MVNETA_GMAC2_PORT_RESET);
new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE |
MVNETA_GMAC_INBAND_RESTART_AN |
MVNETA_GMAC_CONFIG_MII_SPEED |
MVNETA_GMAC_CONFIG_GMII_SPEED |
MVNETA_GMAC_AN_SPEED_EN |
MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL |
MVNETA_GMAC_CONFIG_FLOW_CTRL |
MVNETA_GMAC_AN_FLOW_CTRL_EN |
MVNETA_GMAC_CONFIG_FULL_DUPLEX |
MVNETA_GMAC_AN_DUPLEX_EN);
pp->duplex = phydev->duplex;
pp->speed = phydev->speed;
}
/* Even though it might look weird, when we're configured in
* SGMII or QSGMII mode, the RGMII bit needs to be set.
*/
new_ctrl2 |= MVNETA_GMAC2_PORT_RGMII;
if (state->interface == PHY_INTERFACE_MODE_QSGMII ||
state->interface == PHY_INTERFACE_MODE_SGMII ||
phy_interface_mode_is_8023z(state->interface))
new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE;
if (phylink_test(state->advertising, Pause))
new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
if (state->pause & MLO_PAUSE_TXRX_MASK)
new_an |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
if (!phylink_autoneg_inband(mode)) {
/* Phy or fixed speed */
if (state->duplex)
new_an |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
if (state->speed == SPEED_1000)
new_an |= MVNETA_GMAC_CONFIG_GMII_SPEED;
else if (state->speed == SPEED_100)
new_an |= MVNETA_GMAC_CONFIG_MII_SPEED;
} else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
/* SGMII mode receives the state from the PHY */
new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE;
new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
MVNETA_GMAC_FORCE_LINK_PASS)) |
MVNETA_GMAC_INBAND_AN_ENABLE |
MVNETA_GMAC_AN_SPEED_EN |
MVNETA_GMAC_AN_DUPLEX_EN;
} else {
/* 802.3z negotiation - only 1000base-X */
new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X;
new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
MVNETA_GMAC_FORCE_LINK_PASS)) |
MVNETA_GMAC_INBAND_AN_ENABLE |
MVNETA_GMAC_CONFIG_GMII_SPEED |
/* The MAC only supports FD mode */
MVNETA_GMAC_CONFIG_FULL_DUPLEX;
if (state->pause & MLO_PAUSE_AN && state->an_enabled)
new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN;
}
if (phydev->link != pp->link) {
if (!phydev->link) {
pp->duplex = -1;
pp->speed = 0;
}
/* Armada 370 documentation says we can only change the port mode
* and in-band enable when the link is down, so force it down
* while making these changes. We also do this for GMAC_CTRL2 */
if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X ||
(new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE ||
(new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) {
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
(gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) |
MVNETA_GMAC_FORCE_LINK_DOWN);
}
pp->link = phydev->link;
status_change = 1;
if (new_ctrl0 != gmac_ctrl0)
mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0);
if (new_ctrl2 != gmac_ctrl2)
mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2);
if (new_clk != gmac_clk)
mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk);
if (new_an != gmac_an)
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an);
if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) {
while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) &
MVNETA_GMAC2_PORT_RESET) != 0)
continue;
}
}
if (status_change) {
if (phydev->link) {
if (!pp->use_inband_status) {
u32 val = mvreg_read(pp,
MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
val |= MVNETA_GMAC_FORCE_LINK_PASS;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
val);
}
mvneta_port_up(pp);
} else {
if (!pp->use_inband_status) {
u32 val = mvreg_read(pp,
MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
val |= MVNETA_GMAC_FORCE_LINK_DOWN;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
val);
}
mvneta_port_down(pp);
}
phy_print_status(phydev);
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)
{
struct mvneta_port *pp = netdev_priv(ndev);
u32 val;
mvneta_port_down(pp);
if (!phylink_autoneg_inband(mode)) {
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
val |= MVNETA_GMAC_FORCE_LINK_DOWN;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
}
pp->eee_active = false;
mvneta_set_eee(pp, false);
}
static int mvneta_mdio_probe(struct mvneta_port *pp)
static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode,
struct phy_device *phy)
{
struct phy_device *phy_dev;
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
struct mvneta_port *pp = netdev_priv(ndev);
u32 val;
phy_dev = of_phy_connect(pp->dev, pp->phy_node, mvneta_adjust_link, 0,
pp->phy_interface);
if (!phy_dev) {
netdev_err(pp->dev, "could not find the PHY\n");
return -ENODEV;
if (!phylink_autoneg_inband(mode)) {
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
val |= MVNETA_GMAC_FORCE_LINK_PASS;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
}
phy_ethtool_get_wol(phy_dev, &wol);
device_set_wakeup_capable(&pp->dev->dev, !!wol.supported);
mvneta_port_up(pp);
phy_dev->supported &= PHY_GBIT_FEATURES;
phy_dev->advertising = phy_dev->supported;
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);
}
}
pp->link = 0;
pp->duplex = 0;
pp->speed = 0;
static const struct phylink_mac_ops mvneta_phylink_ops = {
.validate = mvneta_validate,
.mac_link_state = mvneta_mac_link_state,
.mac_an_restart = mvneta_mac_an_restart,
.mac_config = mvneta_mac_config,
.mac_link_down = mvneta_mac_link_down,
.mac_link_up = mvneta_mac_link_up,
};
return 0;
static int mvneta_mdio_probe(struct mvneta_port *pp)
{
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
int err = phylink_of_phy_connect(pp->phylink, pp->dn, 0);
if (err)
netdev_err(pp->dev, "could not attach PHY: %d\n", err);
phylink_ethtool_get_wol(pp->phylink, &wol);
device_set_wakeup_capable(&pp->dev->dev, !!wol.supported);
return err;
}
static void mvneta_mdio_remove(struct mvneta_port *pp)
{
struct net_device *ndev = pp->dev;
phy_disconnect(ndev->phydev);
phylink_disconnect_phy(pp->phylink);
}
/* Electing a CPU must be done in an atomic way: it should be done
......@@ -3455,8 +3568,7 @@ static int mvneta_cpu_online(unsigned int cpu, struct hlist_node *node)
on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
MVNETA_CAUSE_PHY_STATUS_CHANGE |
MVNETA_CAUSE_LINK_CHANGE |
MVNETA_CAUSE_PSC_SYNC_CHANGE);
MVNETA_CAUSE_LINK_CHANGE);
netif_tx_start_all_queues(pp->dev);
spin_unlock(&pp->lock);
return 0;
......@@ -3497,8 +3609,7 @@ static int mvneta_cpu_dead(unsigned int cpu, struct hlist_node *node)
on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
MVNETA_CAUSE_PHY_STATUS_CHANGE |
MVNETA_CAUSE_LINK_CHANGE |
MVNETA_CAUSE_PSC_SYNC_CHANGE);
MVNETA_CAUSE_LINK_CHANGE);
netif_tx_start_all_queues(pp->dev);
return 0;
}
......@@ -3626,10 +3737,9 @@ static int mvneta_stop(struct net_device *dev)
static int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
if (!dev->phydev)
return -ENOTSUPP;
struct mvneta_port *pp = netdev_priv(dev);
return phy_mii_ioctl(dev->phydev, ifr, cmd);
return phylink_mii_ioctl(pp->phylink, ifr, cmd);
}
/* Ethtool methods */
......@@ -3640,44 +3750,25 @@ mvneta_ethtool_set_link_ksettings(struct net_device *ndev,
const struct ethtool_link_ksettings *cmd)
{
struct mvneta_port *pp = netdev_priv(ndev);
struct phy_device *phydev = ndev->phydev;
if (!phydev)
return -ENODEV;
if ((cmd->base.autoneg == AUTONEG_ENABLE) != pp->use_inband_status) {
u32 val;
mvneta_set_autoneg(pp, cmd->base.autoneg == AUTONEG_ENABLE);
if (cmd->base.autoneg == AUTONEG_DISABLE) {
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
MVNETA_GMAC_CONFIG_GMII_SPEED |
MVNETA_GMAC_CONFIG_FULL_DUPLEX);
if (phydev->duplex)
val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
if (phydev->speed == SPEED_1000)
val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
else if (phydev->speed == SPEED_100)
val |= MVNETA_GMAC_CONFIG_MII_SPEED;
return phylink_ethtool_ksettings_set(pp->phylink, cmd);
}
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
}
/* Get link ksettings for ethtools */
static int
mvneta_ethtool_get_link_ksettings(struct net_device *ndev,
struct ethtool_link_ksettings *cmd)
{
struct mvneta_port *pp = netdev_priv(ndev);
pp->use_inband_status = (cmd->base.autoneg == AUTONEG_ENABLE);
netdev_info(pp->dev, "autoneg status set to %i\n",
pp->use_inband_status);
return phylink_ethtool_ksettings_get(pp->phylink, cmd);
}
if (netif_running(ndev)) {
mvneta_port_down(pp);
mvneta_port_up(pp);
}
}
static int mvneta_ethtool_nway_reset(struct net_device *dev)
{
struct mvneta_port *pp = netdev_priv(dev);
return phy_ethtool_ksettings_set(ndev->phydev, cmd);
return phylink_ethtool_nway_reset(pp->phylink);
}
/* Set interrupt coalescing for ethtools */
......@@ -3769,6 +3860,22 @@ static int mvneta_ethtool_set_ringparam(struct net_device *dev,
return 0;
}
static void mvneta_ethtool_get_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *pause)
{
struct mvneta_port *pp = netdev_priv(dev);
phylink_ethtool_get_pauseparam(pp->phylink, pause);
}
static int mvneta_ethtool_set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *pause)
{
struct mvneta_port *pp = netdev_priv(dev);
return phylink_ethtool_set_pauseparam(pp->phylink, pause);
}
static void mvneta_ethtool_get_strings(struct net_device *netdev, u32 sset,
u8 *data)
{
......@@ -3785,26 +3892,35 @@ static void mvneta_ethtool_update_stats(struct mvneta_port *pp)
{
const struct mvneta_statistic *s;
void __iomem *base = pp->base;
u32 high, low, val;
u64 val64;
u32 high, low;
u64 val;
int i;
for (i = 0, s = mvneta_statistics;
s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics);
s++, i++) {
val = 0;
switch (s->type) {
case T_REG_32:
val = readl_relaxed(base + s->offset);
pp->ethtool_stats[i] += val;
break;
case T_REG_64:
/* Docs say to read low 32-bit then high */
low = readl_relaxed(base + s->offset);
high = readl_relaxed(base + s->offset + 4);
val64 = (u64)high << 32 | low;
pp->ethtool_stats[i] += val64;
val = (u64)high << 32 | low;
break;
case T_SW:
switch (s->offset) {
case ETHTOOL_STAT_EEE_WAKEUP:
val = phylink_get_eee_err(pp->phylink);
break;
}
break;
}
pp->ethtool_stats[i] += val;
}
}
......@@ -3939,28 +4055,81 @@ static int mvneta_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
static void mvneta_ethtool_get_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
wol->supported = 0;
wol->wolopts = 0;
struct mvneta_port *pp = netdev_priv(dev);
if (dev->phydev)
phy_ethtool_get_wol(dev->phydev, wol);
phylink_ethtool_get_wol(pp->phylink, wol);
}
static int mvneta_ethtool_set_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
struct mvneta_port *pp = netdev_priv(dev);
int ret;
if (!dev->phydev)
return -EOPNOTSUPP;
ret = phy_ethtool_set_wol(dev->phydev, wol);
ret = phylink_ethtool_set_wol(pp->phylink, wol);
if (!ret)
device_set_wakeup_enable(&dev->dev, !!wol->wolopts);
return ret;
}
static int mvneta_ethtool_get_module_info(struct net_device *dev,
struct ethtool_modinfo *modinfo)
{
struct mvneta_port *pp = netdev_priv(dev);
return phylink_ethtool_get_module_info(pp->phylink, modinfo);
}
static int mvneta_ethtool_get_module_eeprom(struct net_device *dev,
struct ethtool_eeprom *ee, u8 *buf)
{
struct mvneta_port *pp = netdev_priv(dev);
return phylink_ethtool_get_module_eeprom(pp->phylink, ee, buf);
}
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 = {
.ndo_open = mvneta_open,
.ndo_stop = mvneta_stop,
......@@ -3974,13 +4143,15 @@ static const struct net_device_ops mvneta_netdev_ops = {
};
static const struct ethtool_ops mvneta_eth_tool_ops = {
.nway_reset = phy_ethtool_nway_reset,
.nway_reset = mvneta_ethtool_nway_reset,
.get_link = ethtool_op_get_link,
.set_coalesce = mvneta_ethtool_set_coalesce,
.get_coalesce = mvneta_ethtool_get_coalesce,
.get_drvinfo = mvneta_ethtool_get_drvinfo,
.get_ringparam = mvneta_ethtool_get_ringparam,
.set_ringparam = mvneta_ethtool_set_ringparam,
.get_pauseparam = mvneta_ethtool_get_pauseparam,
.set_pauseparam = mvneta_ethtool_set_pauseparam,
.get_strings = mvneta_ethtool_get_strings,
.get_ethtool_stats = mvneta_ethtool_get_stats,
.get_sset_count = mvneta_ethtool_get_sset_count,
......@@ -3988,10 +4159,14 @@ static const struct ethtool_ops mvneta_eth_tool_ops = {
.get_rxnfc = mvneta_ethtool_get_rxnfc,
.get_rxfh = mvneta_ethtool_get_rxfh,
.set_rxfh = mvneta_ethtool_set_rxfh,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.get_link_ksettings = mvneta_ethtool_get_link_ksettings,
.set_link_ksettings = mvneta_ethtool_set_link_ksettings,
.get_wol = mvneta_ethtool_get_wol,
.set_wol = mvneta_ethtool_set_wol,
.get_module_info = mvneta_ethtool_get_module_info,
.get_module_eeprom = mvneta_ethtool_get_module_eeprom,
.get_eee = mvneta_ethtool_get_eee,
.set_eee = mvneta_ethtool_set_eee,
};
/* Initialize hw */
......@@ -4091,42 +4266,16 @@ static void mvneta_conf_mbus_windows(struct mvneta_port *pp,
/* Power up the port */
static int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode)
{
u32 ctrl;
/* MAC Cause register should be cleared */
mvreg_write(pp, MVNETA_UNIT_INTR_CAUSE, 0);
ctrl = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
/* Even though it might look weird, when we're configured in
* SGMII or QSGMII mode, the RGMII bit needs to be set.
*/
switch(phy_mode) {
case PHY_INTERFACE_MODE_QSGMII:
if (phy_mode == PHY_INTERFACE_MODE_QSGMII)
mvreg_write(pp, MVNETA_SERDES_CFG, MVNETA_QSGMII_SERDES_PROTO);
ctrl |= MVNETA_GMAC2_PCS_ENABLE | MVNETA_GMAC2_PORT_RGMII;
break;
case PHY_INTERFACE_MODE_SGMII:
else if (phy_mode == PHY_INTERFACE_MODE_SGMII ||
phy_mode == PHY_INTERFACE_MODE_1000BASEX)
mvreg_write(pp, MVNETA_SERDES_CFG, MVNETA_SGMII_SERDES_PROTO);
ctrl |= MVNETA_GMAC2_PCS_ENABLE | MVNETA_GMAC2_PORT_RGMII;
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
ctrl |= MVNETA_GMAC2_PORT_RGMII;
break;
default:
else if (!phy_interface_mode_is_rgmii(phy_mode))
return -EINVAL;
}
/* Cancel Port Reset */
ctrl &= ~MVNETA_GMAC2_PORT_RESET;
mvreg_write(pp, MVNETA_GMAC_CTRL_2, ctrl);
while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) &
MVNETA_GMAC2_PORT_RESET) != 0)
continue;
return 0;
}
......@@ -4136,14 +4285,13 @@ static int mvneta_probe(struct platform_device *pdev)
{
struct resource *res;
struct device_node *dn = pdev->dev.of_node;
struct device_node *phy_node;
struct device_node *bm_node;
struct mvneta_port *pp;
struct net_device *dev;
struct phylink *phylink;
const char *dt_mac_addr;
char hw_mac_addr[ETH_ALEN];
const char *mac_from;
const char *managed;
int tx_csum_limit;
int phy_mode;
int err;
......@@ -4159,31 +4307,18 @@ static int mvneta_probe(struct platform_device *pdev)
goto err_free_netdev;
}
phy_node = of_parse_phandle(dn, "phy", 0);
if (!phy_node) {
if (!of_phy_is_fixed_link(dn)) {
dev_err(&pdev->dev, "no PHY specified\n");
err = -ENODEV;
goto err_free_irq;
}
err = of_phy_register_fixed_link(dn);
if (err < 0) {
dev_err(&pdev->dev, "cannot register fixed PHY\n");
goto err_free_irq;
}
/* In the case of a fixed PHY, the DT node associated
* to the PHY is the Ethernet MAC DT node.
*/
phy_node = of_node_get(dn);
}
phy_mode = of_get_phy_mode(dn);
if (phy_mode < 0) {
dev_err(&pdev->dev, "incorrect phy-mode\n");
err = -EINVAL;
goto err_put_phy_node;
goto err_free_irq;
}
phylink = phylink_create(dev, pdev->dev.fwnode, phy_mode,
&mvneta_phylink_ops);
if (IS_ERR(phylink)) {
err = PTR_ERR(phylink);
goto err_free_irq;
}
dev->tx_queue_len = MVNETA_MAX_TXD;
......@@ -4194,12 +4329,9 @@ static int mvneta_probe(struct platform_device *pdev)
pp = netdev_priv(dev);
spin_lock_init(&pp->lock);
pp->phy_node = phy_node;
pp->phylink = phylink;
pp->phy_interface = phy_mode;
err = of_property_read_string(dn, "managed", &managed);
pp->use_inband_status = (err == 0 &&
strcmp(managed, "in-band-status") == 0);
pp->dn = dn;
pp->rxq_def = rxq_def;
......@@ -4221,7 +4353,7 @@ static int mvneta_probe(struct platform_device *pdev)
pp->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pp->clk)) {
err = PTR_ERR(pp->clk);
goto err_put_phy_node;
goto err_free_phylink;
}
clk_prepare_enable(pp->clk);
......@@ -4358,14 +4490,6 @@ static int mvneta_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pp->dev);
if (pp->use_inband_status) {
struct phy_device *phy = of_phy_find_device(dn);
mvneta_fixed_link_update(pp, phy);
put_device(&phy->mdio.dev);
}
return 0;
err_netdev:
......@@ -4382,10 +4506,9 @@ static int mvneta_probe(struct platform_device *pdev)
err_clk:
clk_disable_unprepare(pp->clk_bus);
clk_disable_unprepare(pp->clk);
err_put_phy_node:
of_node_put(phy_node);
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
err_free_phylink:
if (pp->phylink)
phylink_destroy(pp->phylink);
err_free_irq:
irq_dispose_mapping(dev->irq);
err_free_netdev:
......@@ -4397,7 +4520,6 @@ static int mvneta_probe(struct platform_device *pdev)
static int mvneta_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct device_node *dn = pdev->dev.of_node;
struct mvneta_port *pp = netdev_priv(dev);
unregister_netdev(dev);
......@@ -4405,10 +4527,8 @@ static int mvneta_remove(struct platform_device *pdev)
clk_disable_unprepare(pp->clk);
free_percpu(pp->ports);
free_percpu(pp->stats);
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
irq_dispose_mapping(dev->irq);
of_node_put(pp->phy_node);
phylink_destroy(pp->phylink);
free_netdev(dev);
if (pp->bm_priv) {
......@@ -4426,8 +4546,10 @@ static int mvneta_suspend(struct device *device)
struct net_device *dev = dev_get_drvdata(device);
struct mvneta_port *pp = netdev_priv(dev);
rtnl_lock();
if (netif_running(dev))
mvneta_stop(dev);
rtnl_unlock();
netif_device_detach(dev);
clk_disable_unprepare(pp->clk_bus);
clk_disable_unprepare(pp->clk);
......@@ -4460,14 +4582,13 @@ static int mvneta_resume(struct device *device)
return err;
}
if (pp->use_inband_status)
mvneta_fixed_link_update(pp, dev->phydev);
netif_device_attach(dev);
rtnl_lock();
if (netif_running(dev)) {
mvneta_open(dev);
mvneta_set_rx_mode(dev);
}
rtnl_unlock();
return 0;
}
......
......@@ -115,37 +115,6 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
}
EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
int fixed_phy_update_state(struct phy_device *phydev,
const struct fixed_phy_status *status,
const struct fixed_phy_status *changed)
{
struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp;
if (!phydev || phydev->mdio.bus != fmb->mii_bus)
return -EINVAL;
list_for_each_entry(fp, &fmb->phys, node) {
if (fp->addr == phydev->mdio.addr) {
write_seqcount_begin(&fp->seqcount);
#define _UPD(x) if (changed->x) \
fp->status.x = status->x
_UPD(link);
_UPD(speed);
_UPD(duplex);
_UPD(pause);
_UPD(asym_pause);
#undef _UPD
fixed_phy_update(fp);
write_seqcount_end(&fp->seqcount);
return 0;
}
}
return -ENOENT;
}
EXPORT_SYMBOL(fixed_phy_update_state);
int fixed_phy_add(unsigned int irq, int phy_addr,
struct fixed_phy_status *status,
int link_gpio)
......
......@@ -24,9 +24,6 @@ extern void fixed_phy_unregister(struct phy_device *phydev);
extern int fixed_phy_set_link_update(struct phy_device *phydev,
int (*link_update)(struct net_device *,
struct fixed_phy_status *));
extern int fixed_phy_update_state(struct phy_device *phydev,
const struct fixed_phy_status *status,
const struct fixed_phy_status *changed);
#else
static inline int fixed_phy_add(unsigned int irq, int phy_id,
struct fixed_phy_status *status,
......@@ -50,12 +47,6 @@ static inline int fixed_phy_set_link_update(struct phy_device *phydev,
{
return -ENODEV;
}
static inline int fixed_phy_update_state(struct phy_device *phydev,
const struct fixed_phy_status *status,
const struct fixed_phy_status *changed)
{
return -ENODEV;
}
#endif /* CONFIG_FIXED_PHY */
#endif /* __PHY_FIXED_H */
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