Skip to content

Commit

Permalink
net: dsa: mt7530: Convert to PHYLINK API
Browse files Browse the repository at this point in the history
Convert mt7530 to PHYLINK API

Signed-off-by: René van Dorst <opensource@vdorst.com>
Tested-by: Frank Wunderlich <frank-w@public-files.de>
Acked-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
vDorst authored and davem330 committed Sep 4, 2019
1 parent 771efed commit ca366d6
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 87 deletions.
266 changes: 197 additions & 69 deletions drivers/net/dsa/mt7530.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/phylink.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
Expand Down Expand Up @@ -633,63 +633,6 @@ mt7530_get_sset_count(struct dsa_switch *ds, int port, int sset)
return ARRAY_SIZE(mt7530_mib);
}

static void mt7530_adjust_link(struct dsa_switch *ds, int port,
struct phy_device *phydev)
{
struct mt7530_priv *priv = ds->priv;

if (phy_is_pseudo_fixed_link(phydev)) {
dev_dbg(priv->dev, "phy-mode for master device = %x\n",
phydev->interface);

/* Setup TX circuit incluing relevant PAD and driving */
mt7530_pad_clk_setup(ds, phydev->interface);

if (priv->id == ID_MT7530) {
/* Setup RX circuit, relevant PAD and driving on the
* host which must be placed after the setup on the
* device side is all finished.
*/
mt7623_pad_clk_setup(ds);
}
} else {
u16 lcl_adv = 0, rmt_adv = 0;
u8 flowctrl;
u32 mcr = PMCR_USERP_LINK | PMCR_FORCE_MODE;

switch (phydev->speed) {
case SPEED_1000:
mcr |= PMCR_FORCE_SPEED_1000;
break;
case SPEED_100:
mcr |= PMCR_FORCE_SPEED_100;
break;
}

if (phydev->link)
mcr |= PMCR_FORCE_LNK;

if (phydev->duplex) {
mcr |= PMCR_FORCE_FDX;

if (phydev->pause)
rmt_adv = LPA_PAUSE_CAP;
if (phydev->asym_pause)
rmt_adv |= LPA_PAUSE_ASYM;

lcl_adv = linkmode_adv_to_lcl_adv_t(
phydev->advertising);
flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);

if (flowctrl & FLOW_CTRL_TX)
mcr |= PMCR_TX_FC_EN;
if (flowctrl & FLOW_CTRL_RX)
mcr |= PMCR_RX_FC_EN;
}
mt7530_write(priv, MT7530_PMCR_P(port), mcr);
}
}

static int
mt7530_cpu_port_enable(struct mt7530_priv *priv,
int port)
Expand All @@ -698,9 +641,6 @@ mt7530_cpu_port_enable(struct mt7530_priv *priv,
mt7530_write(priv, MT7530_PVC_P(port),
PORT_SPEC_TAG);

/* Setup the MAC by default for the cpu port */
mt7530_write(priv, MT7530_PMCR_P(port), PMCR_CPUP_LINK);

/* Disable auto learning on the cpu port */
mt7530_set(priv, MT7530_PSC_P(port), SA_DIS);

Expand Down Expand Up @@ -731,9 +671,6 @@ mt7530_port_enable(struct dsa_switch *ds, int port,

mutex_lock(&priv->reg_mutex);

/* Setup the MAC for the user port */
mt7530_write(priv, MT7530_PMCR_P(port), PMCR_USERP_LINK);

/* Allow the user port gets connected to the cpu port and also
* restore the port matrix if the port is the member of a certain
* bridge.
Expand All @@ -742,7 +679,7 @@ mt7530_port_enable(struct dsa_switch *ds, int port,
priv->ports[port].enable = true;
mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
priv->ports[port].pm);
mt7530_port_set_status(priv, port, 1);
mt7530_port_set_status(priv, port, 0);

mutex_unlock(&priv->reg_mutex);

Expand Down Expand Up @@ -1232,10 +1169,10 @@ static int
mt7530_setup(struct dsa_switch *ds)
{
struct mt7530_priv *priv = ds->priv;
int ret, i;
u32 id, val;
struct device_node *dn;
struct mt7530_dummy_poll p;
struct device_node *dn;
u32 id, val;
int ret, i;

/* The parent node of master netdev which holds the common system
* controller also is the container for two GMACs nodes representing
Expand Down Expand Up @@ -1305,6 +1242,8 @@ mt7530_setup(struct dsa_switch *ds)
val |= MHWTRAP_MANUAL;
mt7530_write(priv, MT7530_MHWTRAP, val);

priv->p6_interface = PHY_INTERFACE_MODE_NA;

/* Enable and reset MIB counters */
mt7530_mib_reset(ds);

Expand All @@ -1329,6 +1268,191 @@ mt7530_setup(struct dsa_switch *ds)
return 0;
}

static void mt7530_phylink_mac_config(struct dsa_switch *ds, int port,
unsigned int mode,
const struct phylink_link_state *state)
{
struct mt7530_priv *priv = ds->priv;
u32 mcr_cur, mcr_new;

switch (port) {
case 0: /* Internal phy */
case 1:
case 2:
case 3:
case 4:
if (state->interface != PHY_INTERFACE_MODE_GMII)
return;
break;
/* case 5: Port 5 is not supported! */
case 6: /* 1st cpu port */
if (priv->p6_interface == state->interface)
break;

if (state->interface != PHY_INTERFACE_MODE_RGMII &&
state->interface != PHY_INTERFACE_MODE_TRGMII)
return;

/* Setup TX circuit incluing relevant PAD and driving */
mt7530_pad_clk_setup(ds, state->interface);

if (priv->id == ID_MT7530) {
/* Setup RX circuit, relevant PAD and driving on the
* host which must be placed after the setup on the
* device side is all finished.
*/
mt7623_pad_clk_setup(ds);
}

priv->p6_interface = state->interface;
break;
default:
dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
return;
}

if (phylink_autoneg_inband(mode)) {
dev_err(ds->dev, "%s: in-band negotiation unsupported\n",
__func__);
return;
}

mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
mcr_new = mcr_cur;
mcr_new &= ~(PMCR_FORCE_SPEED_1000 | PMCR_FORCE_SPEED_100 |
PMCR_FORCE_FDX | PMCR_TX_FC_EN | PMCR_RX_FC_EN);
mcr_new |= PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | PMCR_BACKOFF_EN |
PMCR_BACKPR_EN | PMCR_FORCE_MODE | PMCR_FORCE_LNK;

switch (state->speed) {
case SPEED_1000:
mcr_new |= PMCR_FORCE_SPEED_1000;
break;
case SPEED_100:
mcr_new |= PMCR_FORCE_SPEED_100;
break;
}
if (state->duplex == DUPLEX_FULL) {
mcr_new |= PMCR_FORCE_FDX;
if (state->pause & MLO_PAUSE_TX)
mcr_new |= PMCR_TX_FC_EN;
if (state->pause & MLO_PAUSE_RX)
mcr_new |= PMCR_RX_FC_EN;
}

if (mcr_new != mcr_cur)
mt7530_write(priv, MT7530_PMCR_P(port), mcr_new);
}

static void mt7530_phylink_mac_link_down(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface)
{
struct mt7530_priv *priv = ds->priv;

mt7530_port_set_status(priv, port, 0);
}

static void mt7530_phylink_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev)
{
struct mt7530_priv *priv = ds->priv;

mt7530_port_set_status(priv, port, 1);
}

static void mt7530_phylink_validate(struct dsa_switch *ds, int port,
unsigned long *supported,
struct phylink_link_state *state)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };

switch (port) {
case 0: /* Internal phy */
case 1:
case 2:
case 3:
case 4:
if (state->interface != PHY_INTERFACE_MODE_NA &&
state->interface != PHY_INTERFACE_MODE_GMII)
goto unsupported;
break;
/* case 5: Port 5 not supported! */
case 6: /* 1st cpu port */
if (state->interface != PHY_INTERFACE_MODE_NA &&
state->interface != PHY_INTERFACE_MODE_RGMII &&
state->interface != PHY_INTERFACE_MODE_TRGMII)
goto unsupported;
break;
default:
dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
unsupported:
linkmode_zero(supported);
return;
}

phylink_set_port_modes(mask);
phylink_set(mask, Autoneg);

if (state->interface != PHY_INTERFACE_MODE_TRGMII) {
phylink_set(mask, 10baseT_Half);
phylink_set(mask, 10baseT_Full);
phylink_set(mask, 100baseT_Half);
phylink_set(mask, 100baseT_Full);
phylink_set(mask, 1000baseT_Half);
}

phylink_set(mask, 1000baseT_Full);

phylink_set(mask, Pause);
phylink_set(mask, Asym_Pause);

linkmode_and(supported, supported, mask);
linkmode_and(state->advertising, state->advertising, mask);
}

static int
mt7530_phylink_mac_link_state(struct dsa_switch *ds, int port,
struct phylink_link_state *state)
{
struct mt7530_priv *priv = ds->priv;
u32 pmsr;

if (port < 0 || port >= MT7530_NUM_PORTS)
return -EINVAL;

pmsr = mt7530_read(priv, MT7530_PMSR_P(port));

state->link = (pmsr & PMSR_LINK);
state->an_complete = state->link;
state->duplex = !!(pmsr & PMSR_DPX);

switch (pmsr & PMSR_SPEED_MASK) {
case PMSR_SPEED_10:
state->speed = SPEED_10;
break;
case PMSR_SPEED_100:
state->speed = SPEED_100;
break;
case PMSR_SPEED_1000:
state->speed = SPEED_1000;
break;
default:
state->speed = SPEED_UNKNOWN;
break;
}

state->pause &= ~(MLO_PAUSE_RX | MLO_PAUSE_TX);
if (pmsr & PMSR_RX_FC)
state->pause |= MLO_PAUSE_RX;
if (pmsr & PMSR_TX_FC)
state->pause |= MLO_PAUSE_TX;

return 1;
}

static const struct dsa_switch_ops mt7530_switch_ops = {
.get_tag_protocol = mtk_get_tag_protocol,
.setup = mt7530_setup,
Expand All @@ -1337,7 +1461,6 @@ static const struct dsa_switch_ops mt7530_switch_ops = {
.phy_write = mt7530_phy_write,
.get_ethtool_stats = mt7530_get_ethtool_stats,
.get_sset_count = mt7530_get_sset_count,
.adjust_link = mt7530_adjust_link,
.port_enable = mt7530_port_enable,
.port_disable = mt7530_port_disable,
.port_stp_state_set = mt7530_stp_state_set,
Expand All @@ -1350,6 +1473,11 @@ static const struct dsa_switch_ops mt7530_switch_ops = {
.port_vlan_prepare = mt7530_port_vlan_prepare,
.port_vlan_add = mt7530_port_vlan_add,
.port_vlan_del = mt7530_port_vlan_del,
.phylink_validate = mt7530_phylink_validate,
.phylink_mac_link_state = mt7530_phylink_mac_link_state,
.phylink_mac_config = mt7530_phylink_mac_config,
.phylink_mac_link_down = mt7530_phylink_mac_link_down,
.phylink_mac_link_up = mt7530_phylink_mac_link_up,
};

static const struct of_device_id mt7530_of_match[] = {
Expand Down
32 changes: 14 additions & 18 deletions drivers/net/dsa/mt7530.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,26 +198,20 @@ enum mt7530_vlan_port_attr {
#define PMCR_FORCE_SPEED_100 BIT(2)
#define PMCR_FORCE_FDX BIT(1)
#define PMCR_FORCE_LNK BIT(0)
#define PMCR_COMMON_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \
PMCR_TX_EN | PMCR_RX_EN | \
PMCR_TX_FC_EN | PMCR_RX_FC_EN)
#define PMCR_CPUP_LINK (PMCR_COMMON_LINK | PMCR_FORCE_MODE | \
PMCR_FORCE_SPEED_1000 | \
PMCR_FORCE_FDX | \
PMCR_FORCE_LNK)
#define PMCR_USERP_LINK PMCR_COMMON_LINK
#define PMCR_FIXED_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
PMCR_FORCE_MODE | PMCR_TX_EN | \
PMCR_RX_EN | PMCR_BACKPR_EN | \
PMCR_BACKOFF_EN | \
PMCR_FORCE_SPEED_1000 | \
PMCR_FORCE_FDX | \
PMCR_FORCE_LNK)
#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \
PMCR_TX_FC_EN | PMCR_RX_FC_EN)
#define PMCR_SPEED_MASK (PMCR_FORCE_SPEED_100 | \
PMCR_FORCE_SPEED_1000)

#define MT7530_PMSR_P(x) (0x3008 + (x) * 0x100)
#define PMSR_EEE1G BIT(7)
#define PMSR_EEE100M BIT(6)
#define PMSR_RX_FC BIT(5)
#define PMSR_TX_FC BIT(4)
#define PMSR_SPEED_1000 BIT(3)
#define PMSR_SPEED_100 BIT(2)
#define PMSR_SPEED_10 0x00
#define PMSR_SPEED_MASK (PMSR_SPEED_100 | PMSR_SPEED_1000)
#define PMSR_DPX BIT(1)
#define PMSR_LINK BIT(0)

/* Register for MIB */
#define MT7530_PORT_MIB_COUNTER(x) (0x4000 + (x) * 0x100)
Expand Down Expand Up @@ -423,6 +417,7 @@ struct mt7530_port {
* @ports: Holding the state among ports
* @reg_mutex: The lock for protecting among process accessing
* registers
* @p6_interface Holding the current port 6 interface
*/
struct mt7530_priv {
struct device *dev;
Expand All @@ -435,6 +430,7 @@ struct mt7530_priv {
struct gpio_desc *reset;
unsigned int id;
bool mcm;
phy_interface_t p6_interface;

struct mt7530_port ports[MT7530_NUM_PORTS];
/* protect among processes for registers access*/
Expand Down

0 comments on commit ca366d6

Please sign in to comment.