Commit 2227ec7b authored by David S. Miller's avatar David S. Miller

Merge branch 'sja1105-xpcs'

Vladimir Oltean says:

====================
Port the SJA1105 DSA driver to XPCS

As requested when adding support for the NXP SJA1110, the SJA1105 driver
could make use of the common XPCS driver, to eliminate some hardware
specific code duplication.

This series modifies the XPCS driver so that it can accommodate the XPCS
instantiation from NXP switches, and the SJA1105 driver so it can expose
what the XPCS driver expects.

Tested on NXP SJA1105S and SJA1110A.

Changes in v3:
None. This is a resend of v2 which had "changes requested" even though
there was no direct feedback.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a6e49699 56b63466
...@@ -13203,6 +13203,7 @@ M: Vladimir Oltean <olteanv@gmail.com> ...@@ -13203,6 +13203,7 @@ M: Vladimir Oltean <olteanv@gmail.com>
L: linux-kernel@vger.kernel.org L: linux-kernel@vger.kernel.org
S: Maintained S: Maintained
F: drivers/net/dsa/sja1105 F: drivers/net/dsa/sja1105
F: drivers/net/pcs/pcs-xpcs-nxp.c
NXP TDA998X DRM DRIVER NXP TDA998X DRM DRIVER
M: Russell King <linux@armlinux.org.uk> M: Russell King <linux@armlinux.org.uk>
...@@ -17676,6 +17677,7 @@ M: Jose Abreu <Jose.Abreu@synopsys.com> ...@@ -17676,6 +17677,7 @@ M: Jose Abreu <Jose.Abreu@synopsys.com>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
S: Supported S: Supported
F: drivers/net/pcs/pcs-xpcs.c F: drivers/net/pcs/pcs-xpcs.c
F: drivers/net/pcs/pcs-xpcs.h
F: include/linux/pcs/pcs-xpcs.h F: include/linux/pcs/pcs-xpcs.h
SYNOPSYS DESIGNWARE I2C DRIVER SYNOPSYS DESIGNWARE I2C DRIVER
......
...@@ -3,6 +3,7 @@ config NET_DSA_SJA1105 ...@@ -3,6 +3,7 @@ config NET_DSA_SJA1105
tristate "NXP SJA1105 Ethernet switch family support" tristate "NXP SJA1105 Ethernet switch family support"
depends on NET_DSA && SPI depends on NET_DSA && SPI
select NET_DSA_TAG_SJA1105 select NET_DSA_TAG_SJA1105
select PCS_XPCS
select PACKING select PACKING
select CRC32 select CRC32
help help
......
...@@ -69,6 +69,7 @@ struct sja1105_regs { ...@@ -69,6 +69,7 @@ struct sja1105_regs {
u64 stats[__MAX_SJA1105_STATS_AREA][SJA1105_MAX_NUM_PORTS]; u64 stats[__MAX_SJA1105_STATS_AREA][SJA1105_MAX_NUM_PORTS];
u64 mdio_100base_tx; u64 mdio_100base_tx;
u64 mdio_100base_t1; u64 mdio_100base_t1;
u64 pcs_base[SJA1105_MAX_NUM_PORTS];
}; };
struct sja1105_mdio_private { struct sja1105_mdio_private {
...@@ -133,6 +134,8 @@ struct sja1105_info { ...@@ -133,6 +134,8 @@ struct sja1105_info {
bool (*rxtstamp)(struct dsa_switch *ds, int port, struct sk_buff *skb); bool (*rxtstamp)(struct dsa_switch *ds, int port, struct sk_buff *skb);
void (*txtstamp)(struct dsa_switch *ds, int port, struct sk_buff *skb); void (*txtstamp)(struct dsa_switch *ds, int port, struct sk_buff *skb);
int (*clocking_setup)(struct sja1105_private *priv); int (*clocking_setup)(struct sja1105_private *priv);
int (*pcs_mdio_read)(struct mii_bus *bus, int phy, int reg);
int (*pcs_mdio_write)(struct mii_bus *bus, int phy, int reg, u16 val);
const char *name; const char *name;
bool supports_mii[SJA1105_MAX_NUM_PORTS]; bool supports_mii[SJA1105_MAX_NUM_PORTS];
bool supports_rmii[SJA1105_MAX_NUM_PORTS]; bool supports_rmii[SJA1105_MAX_NUM_PORTS];
...@@ -265,6 +268,8 @@ struct sja1105_private { ...@@ -265,6 +268,8 @@ struct sja1105_private {
struct sja1105_cbs_entry *cbs; struct sja1105_cbs_entry *cbs;
struct mii_bus *mdio_base_t1; struct mii_bus *mdio_base_t1;
struct mii_bus *mdio_base_tx; struct mii_bus *mdio_base_tx;
struct mii_bus *mdio_pcs;
struct dw_xpcs *xpcs[SJA1105_MAX_NUM_PORTS];
struct sja1105_tagger_data tagger_data; struct sja1105_tagger_data tagger_data;
struct sja1105_ptp_data ptp_data; struct sja1105_ptp_data ptp_data;
struct sja1105_tas_data tas_data; struct sja1105_tas_data tas_data;
...@@ -297,6 +302,10 @@ void sja1105_frame_memory_partitioning(struct sja1105_private *priv); ...@@ -297,6 +302,10 @@ void sja1105_frame_memory_partitioning(struct sja1105_private *priv);
/* From sja1105_mdio.c */ /* From sja1105_mdio.c */
int sja1105_mdiobus_register(struct dsa_switch *ds); int sja1105_mdiobus_register(struct dsa_switch *ds);
void sja1105_mdiobus_unregister(struct dsa_switch *ds); void sja1105_mdiobus_unregister(struct dsa_switch *ds);
int sja1105_pcs_mdio_read(struct mii_bus *bus, int phy, int reg);
int sja1105_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val);
int sja1110_pcs_mdio_read(struct mii_bus *bus, int phy, int reg);
int sja1110_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val);
/* From sja1105_devlink.c */ /* From sja1105_devlink.c */
int sja1105_devlink_setup(struct dsa_switch *ds); int sja1105_devlink_setup(struct dsa_switch *ds);
......
...@@ -16,13 +16,13 @@ ...@@ -16,13 +16,13 @@
#include <linux/of_net.h> #include <linux/of_net.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pcs/pcs-xpcs.h>
#include <linux/netdev_features.h> #include <linux/netdev_features.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/dsa/8021q.h> #include <linux/dsa/8021q.h>
#include "sja1105.h" #include "sja1105.h"
#include "sja1105_sgmii.h"
#include "sja1105_tas.h" #include "sja1105_tas.h"
#define SJA1105_UNKNOWN_MULTICAST 0x010000000000ull #define SJA1105_UNKNOWN_MULTICAST 0x010000000000ull
...@@ -209,12 +209,14 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv) ...@@ -209,12 +209,14 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv)
goto unsupported; goto unsupported;
mii->xmii_mode[i] = XMII_MODE_SGMII; mii->xmii_mode[i] = XMII_MODE_SGMII;
mii->special[i] = true;
break; break;
case PHY_INTERFACE_MODE_2500BASEX: case PHY_INTERFACE_MODE_2500BASEX:
if (!priv->info->supports_2500basex[i]) if (!priv->info->supports_2500basex[i])
goto unsupported; goto unsupported;
mii->xmii_mode[i] = XMII_MODE_SGMII; mii->xmii_mode[i] = XMII_MODE_SGMII;
mii->special[i] = true;
break; break;
unsupported: unsupported:
default: default:
...@@ -1002,93 +1004,6 @@ static int sja1105_parse_dt(struct sja1105_private *priv) ...@@ -1002,93 +1004,6 @@ static int sja1105_parse_dt(struct sja1105_private *priv)
return rc; return rc;
} }
static int sja1105_sgmii_read(struct sja1105_private *priv, int port, int mmd,
int pcs_reg)
{
u64 addr = (mmd << 16) | pcs_reg;
u32 val;
int rc;
if (port != SJA1105_SGMII_PORT)
return -ENODEV;
rc = sja1105_xfer_u32(priv, SPI_READ, addr, &val, NULL);
if (rc < 0)
return rc;
return val;
}
static int sja1105_sgmii_write(struct sja1105_private *priv, int port, int mmd,
int pcs_reg, u16 pcs_val)
{
u64 addr = (mmd << 16) | pcs_reg;
u32 val = pcs_val;
int rc;
if (port != SJA1105_SGMII_PORT)
return -ENODEV;
rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &val, NULL);
if (rc < 0)
return rc;
return val;
}
static void sja1105_sgmii_pcs_config(struct sja1105_private *priv, int port,
bool an_enabled, bool an_master)
{
u16 ac = SJA1105_AC_AUTONEG_MODE_SGMII;
/* DIGITAL_CONTROL_1: Enable vendor-specific MMD1, allow the PHY to
* stop the clock during LPI mode, make the MAC reconfigure
* autonomously after PCS autoneg is done, flush the internal FIFOs.
*/
sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, SJA1105_DC1,
SJA1105_DC1_EN_VSMMD1 |
SJA1105_DC1_CLOCK_STOP_EN |
SJA1105_DC1_MAC_AUTO_SW |
SJA1105_DC1_INIT);
/* DIGITAL_CONTROL_2: No polarity inversion for TX and RX lanes */
sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, SJA1105_DC2,
SJA1105_DC2_TX_POL_INV_DISABLE);
/* AUTONEG_CONTROL: Use SGMII autoneg */
if (an_master)
ac |= SJA1105_AC_PHY_MODE | SJA1105_AC_SGMII_LINK;
sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, SJA1105_AC, ac);
/* BASIC_CONTROL: enable in-band AN now, if requested. Otherwise,
* sja1105_sgmii_pcs_force_speed must be called later for the link
* to become operational.
*/
if (an_enabled)
sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1,
BMCR_ANENABLE | BMCR_ANRESTART);
}
static void sja1105_sgmii_pcs_force_speed(struct sja1105_private *priv,
int port, int speed)
{
int pcs_speed;
switch (speed) {
case SPEED_1000:
pcs_speed = BMCR_SPEED1000;
break;
case SPEED_100:
pcs_speed = BMCR_SPEED100;
break;
case SPEED_10:
pcs_speed = BMCR_SPEED10;
break;
default:
dev_err(priv->ds->dev, "Invalid speed %d\n", speed);
return;
}
sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1,
pcs_speed | BMCR_FULLDPLX);
}
/* Convert link speed from SJA1105 to ethtool encoding */ /* Convert link speed from SJA1105 to ethtool encoding */
static int sja1105_port_speed_to_ethtool(struct sja1105_private *priv, static int sja1105_port_speed_to_ethtool(struct sja1105_private *priv,
u64 speed) u64 speed)
...@@ -1141,6 +1056,9 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, ...@@ -1141,6 +1056,9 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
case SPEED_1000: case SPEED_1000:
speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS];
break; break;
case SPEED_2500:
speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
break;
default: default:
dev_err(dev, "Invalid speed %iMbps\n", speed_mbps); dev_err(dev, "Invalid speed %iMbps\n", speed_mbps);
return -EINVAL; return -EINVAL;
...@@ -1155,6 +1073,8 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, ...@@ -1155,6 +1073,8 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
*/ */
if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII) if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII)
mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS];
else if (priv->phy_mode[port] == PHY_INTERFACE_MODE_2500BASEX)
mac[port].speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
else else
mac[port].speed = speed; mac[port].speed = speed;
...@@ -1195,10 +1115,9 @@ static void sja1105_mac_config(struct dsa_switch *ds, int port, ...@@ -1195,10 +1115,9 @@ static void sja1105_mac_config(struct dsa_switch *ds, int port,
unsigned int mode, unsigned int mode,
const struct phylink_link_state *state) const struct phylink_link_state *state)
{ {
struct dsa_port *dp = dsa_to_port(ds, port);
struct sja1105_private *priv = ds->priv; struct sja1105_private *priv = ds->priv;
bool is_sgmii; struct dw_xpcs *xpcs;
is_sgmii = (state->interface == PHY_INTERFACE_MODE_SGMII);
if (sja1105_phy_mode_mismatch(priv, port, state->interface)) { if (sja1105_phy_mode_mismatch(priv, port, state->interface)) {
dev_err(ds->dev, "Changing PHY mode to %s not supported!\n", dev_err(ds->dev, "Changing PHY mode to %s not supported!\n",
...@@ -1206,15 +1125,10 @@ static void sja1105_mac_config(struct dsa_switch *ds, int port, ...@@ -1206,15 +1125,10 @@ static void sja1105_mac_config(struct dsa_switch *ds, int port,
return; return;
} }
if (phylink_autoneg_inband(mode) && !is_sgmii) { xpcs = priv->xpcs[port];
dev_err(ds->dev, "In-band AN not supported!\n");
return;
}
if (is_sgmii) if (xpcs)
sja1105_sgmii_pcs_config(priv, port, phylink_set_pcs(dp->pl, &xpcs->pcs);
phylink_autoneg_inband(mode),
false);
} }
static void sja1105_mac_link_down(struct dsa_switch *ds, int port, static void sja1105_mac_link_down(struct dsa_switch *ds, int port,
...@@ -1235,10 +1149,6 @@ static void sja1105_mac_link_up(struct dsa_switch *ds, int port, ...@@ -1235,10 +1149,6 @@ static void sja1105_mac_link_up(struct dsa_switch *ds, int port,
sja1105_adjust_port_config(priv, port, speed); sja1105_adjust_port_config(priv, port, speed);
if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII &&
!phylink_autoneg_inband(mode))
sja1105_sgmii_pcs_force_speed(priv, port, speed);
sja1105_inhibit_tx(priv, BIT(port), false); sja1105_inhibit_tx(priv, BIT(port), false);
} }
...@@ -1277,44 +1187,16 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port, ...@@ -1277,44 +1187,16 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port,
if (mii->xmii_mode[port] == XMII_MODE_RGMII || if (mii->xmii_mode[port] == XMII_MODE_RGMII ||
mii->xmii_mode[port] == XMII_MODE_SGMII) mii->xmii_mode[port] == XMII_MODE_SGMII)
phylink_set(mask, 1000baseT_Full); phylink_set(mask, 1000baseT_Full);
if (priv->info->supports_2500basex[port]) {
phylink_set(mask, 2500baseT_Full);
phylink_set(mask, 2500baseX_Full);
}
bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_and(state->advertising, state->advertising, mask, bitmap_and(state->advertising, state->advertising, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS); __ETHTOOL_LINK_MODE_MASK_NBITS);
} }
static int sja1105_mac_pcs_get_state(struct dsa_switch *ds, int port,
struct phylink_link_state *state)
{
struct sja1105_private *priv = ds->priv;
int ais;
/* Read the vendor-specific AUTONEG_INTR_STATUS register */
ais = sja1105_sgmii_read(priv, port, MDIO_MMD_VEND2, SJA1105_AIS);
if (ais < 0)
return ais;
switch (SJA1105_AIS_SPEED(ais)) {
case 0:
state->speed = SPEED_10;
break;
case 1:
state->speed = SPEED_100;
break;
case 2:
state->speed = SPEED_1000;
break;
default:
dev_err(ds->dev, "Invalid SGMII PCS speed %lu\n",
SJA1105_AIS_SPEED(ais));
}
state->duplex = SJA1105_AIS_DUPLEX_MODE(ais);
state->an_complete = SJA1105_AIS_COMPLETE(ais);
state->link = SJA1105_AIS_LINK_STATUS(ais);
return 0;
}
static int static int
sja1105_find_static_fdb_entry(struct sja1105_private *priv, int port, sja1105_find_static_fdb_entry(struct sja1105_private *priv, int port,
const struct sja1105_l2_lookup_entry *requested) const struct sja1105_l2_lookup_entry *requested)
...@@ -1990,14 +1872,14 @@ int sja1105_static_config_reload(struct sja1105_private *priv, ...@@ -1990,14 +1872,14 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
* change it through the dynamic interface later. * change it through the dynamic interface later.
*/ */
for (i = 0; i < ds->num_ports; i++) { for (i = 0; i < ds->num_ports; i++) {
u32 reg_addr = mdiobus_c45_addr(MDIO_MMD_VEND2, MDIO_CTRL1);
speed_mbps[i] = sja1105_port_speed_to_ethtool(priv, speed_mbps[i] = sja1105_port_speed_to_ethtool(priv,
mac[i].speed); mac[i].speed);
mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO];
if (priv->phy_mode[i] == PHY_INTERFACE_MODE_SGMII) if (priv->xpcs[i])
bmcr[i] = sja1105_sgmii_read(priv, i, bmcr[i] = mdiobus_read(priv->mdio_pcs, i, reg_addr);
MDIO_MMD_VEND2,
MDIO_CTRL1);
} }
/* No PTP operations can run right now */ /* No PTP operations can run right now */
...@@ -2045,30 +1927,41 @@ int sja1105_static_config_reload(struct sja1105_private *priv, ...@@ -2045,30 +1927,41 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
goto out; goto out;
for (i = 0; i < ds->num_ports; i++) { for (i = 0; i < ds->num_ports; i++) {
bool an_enabled; struct dw_xpcs *xpcs = priv->xpcs[i];
unsigned int mode;
rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]); rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]);
if (rc < 0) if (rc < 0)
goto out; goto out;
if (priv->phy_mode[i] != PHY_INTERFACE_MODE_SGMII) if (!xpcs)
continue; continue;
an_enabled = !!(bmcr[i] & BMCR_ANENABLE); if (bmcr[i] & BMCR_ANENABLE)
mode = MLO_AN_INBAND;
else if (priv->fixed_link[i])
mode = MLO_AN_FIXED;
else
mode = MLO_AN_PHY;
sja1105_sgmii_pcs_config(priv, i, an_enabled, false); rc = xpcs_do_config(xpcs, priv->phy_mode[i], mode);
if (rc < 0)
goto out;
if (!an_enabled) { if (!phylink_autoneg_inband(mode)) {
int speed = SPEED_UNKNOWN; int speed = SPEED_UNKNOWN;
if (bmcr[i] & BMCR_SPEED1000) if (priv->phy_mode[i] == PHY_INTERFACE_MODE_2500BASEX)
speed = SPEED_2500;
else if (bmcr[i] & BMCR_SPEED1000)
speed = SPEED_1000; speed = SPEED_1000;
else if (bmcr[i] & BMCR_SPEED100) else if (bmcr[i] & BMCR_SPEED100)
speed = SPEED_100; speed = SPEED_100;
else else
speed = SPEED_10; speed = SPEED_10;
sja1105_sgmii_pcs_force_speed(priv, i, speed); xpcs_link_up(&xpcs->pcs, mode, priv->phy_mode[i],
speed, DUPLEX_FULL);
} }
} }
...@@ -3649,7 +3542,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = { ...@@ -3649,7 +3542,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
.port_change_mtu = sja1105_change_mtu, .port_change_mtu = sja1105_change_mtu,
.port_max_mtu = sja1105_get_max_mtu, .port_max_mtu = sja1105_get_max_mtu,
.phylink_validate = sja1105_phylink_validate, .phylink_validate = sja1105_phylink_validate,
.phylink_mac_link_state = sja1105_mac_pcs_get_state,
.phylink_mac_config = sja1105_mac_config, .phylink_mac_config = sja1105_mac_config,
.phylink_mac_link_up = sja1105_mac_link_up, .phylink_mac_link_up = sja1105_mac_link_up,
.phylink_mac_link_down = sja1105_mac_link_down, .phylink_mac_link_down = sja1105_mac_link_down,
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/* Copyright 2021, NXP Semiconductors /* Copyright 2021, NXP Semiconductors
*/ */
#include <linux/pcs/pcs-xpcs.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include "sja1105.h" #include "sja1105.h"
#define SJA1110_PCS_BANK_REG SJA1110_SPI_ADDR(0x3fc)
int sja1105_pcs_mdio_read(struct mii_bus *bus, int phy, int reg)
{
struct sja1105_mdio_private *mdio_priv = bus->priv;
struct sja1105_private *priv = mdio_priv->priv;
u64 addr;
u32 tmp;
u16 mmd;
int rc;
if (!(reg & MII_ADDR_C45))
return -EINVAL;
mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
addr = (mmd << 16) | (reg & GENMASK(15, 0));
if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
return 0xffff;
if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID1)
return NXP_SJA1105_XPCS_ID >> 16;
if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID2)
return NXP_SJA1105_XPCS_ID & GENMASK(15, 0);
rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
if (rc < 0)
return rc;
return tmp & 0xffff;
}
int sja1105_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
{
struct sja1105_mdio_private *mdio_priv = bus->priv;
struct sja1105_private *priv = mdio_priv->priv;
u64 addr;
u32 tmp;
u16 mmd;
if (!(reg & MII_ADDR_C45))
return -EINVAL;
mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
addr = (mmd << 16) | (reg & GENMASK(15, 0));
tmp = val;
if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
return -EINVAL;
return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
}
int sja1110_pcs_mdio_read(struct mii_bus *bus, int phy, int reg)
{
struct sja1105_mdio_private *mdio_priv = bus->priv;
struct sja1105_private *priv = mdio_priv->priv;
const struct sja1105_regs *regs = priv->info->regs;
int offset, bank;
u64 addr;
u32 tmp;
u16 mmd;
int rc;
if (!(reg & MII_ADDR_C45))
return -EINVAL;
if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
return -ENODEV;
mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
addr = (mmd << 16) | (reg & GENMASK(15, 0));
if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID1)
return NXP_SJA1110_XPCS_ID >> 16;
if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID2)
return NXP_SJA1110_XPCS_ID & GENMASK(15, 0);
bank = addr >> 8;
offset = addr & GENMASK(7, 0);
/* This addressing scheme reserves register 0xff for the bank address
* register, so that can never be addressed.
*/
if (WARN_ON(offset == 0xff))
return -ENODEV;
tmp = bank;
rc = sja1105_xfer_u32(priv, SPI_WRITE,
regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
&tmp, NULL);
if (rc < 0)
return rc;
rc = sja1105_xfer_u32(priv, SPI_READ, regs->pcs_base[phy] + offset,
&tmp, NULL);
if (rc < 0)
return rc;
return tmp & 0xffff;
}
int sja1110_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
{
struct sja1105_mdio_private *mdio_priv = bus->priv;
struct sja1105_private *priv = mdio_priv->priv;
const struct sja1105_regs *regs = priv->info->regs;
int offset, bank;
u64 addr;
u32 tmp;
u16 mmd;
int rc;
if (!(reg & MII_ADDR_C45))
return -EINVAL;
if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
return -ENODEV;
mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
addr = (mmd << 16) | (reg & GENMASK(15, 0));
bank = addr >> 8;
offset = addr & GENMASK(7, 0);
/* This addressing scheme reserves register 0xff for the bank address
* register, so that can never be addressed.
*/
if (WARN_ON(offset == 0xff))
return -ENODEV;
tmp = bank;
rc = sja1105_xfer_u32(priv, SPI_WRITE,
regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
&tmp, NULL);
if (rc < 0)
return rc;
tmp = val;
return sja1105_xfer_u32(priv, SPI_WRITE, regs->pcs_base[phy] + offset,
&tmp, NULL);
}
enum sja1105_mdio_opcode { enum sja1105_mdio_opcode {
SJA1105_C45_ADDR = 0, SJA1105_C45_ADDR = 0,
SJA1105_C22 = 1, SJA1105_C22 = 1,
...@@ -239,6 +386,108 @@ static void sja1105_mdiobus_base_t1_unregister(struct sja1105_private *priv) ...@@ -239,6 +386,108 @@ static void sja1105_mdiobus_base_t1_unregister(struct sja1105_private *priv)
priv->mdio_base_t1 = NULL; priv->mdio_base_t1 = NULL;
} }
static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
{
struct sja1105_mdio_private *mdio_priv;
struct dsa_switch *ds = priv->ds;
struct mii_bus *bus;
int rc = 0;
int port;
if (!priv->info->pcs_mdio_read || !priv->info->pcs_mdio_write)
return 0;
bus = mdiobus_alloc_size(sizeof(*mdio_priv));
if (!bus)
return -ENOMEM;
bus->name = "SJA1105 PCS MDIO bus";
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-pcs",
dev_name(ds->dev));
bus->read = priv->info->pcs_mdio_read;
bus->write = priv->info->pcs_mdio_write;
bus->parent = ds->dev;
/* There is no PHY on this MDIO bus => mask out all PHY addresses
* from auto probing.
*/
bus->phy_mask = ~0;
mdio_priv = bus->priv;
mdio_priv->priv = priv;
rc = mdiobus_register(bus);
if (rc) {
mdiobus_free(bus);
return rc;
}
for (port = 0; port < ds->num_ports; port++) {
struct mdio_device *mdiodev;
struct dw_xpcs *xpcs;
if (dsa_is_unused_port(ds, port))
continue;
if (priv->phy_mode[port] != PHY_INTERFACE_MODE_SGMII &&
priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX)
continue;
mdiodev = mdio_device_create(bus, port);
if (IS_ERR(mdiodev)) {
rc = PTR_ERR(mdiodev);
goto out_pcs_free;
}
xpcs = xpcs_create(mdiodev, priv->phy_mode[port]);
if (IS_ERR(xpcs)) {
rc = PTR_ERR(xpcs);
goto out_pcs_free;
}
priv->xpcs[port] = xpcs;
}
priv->mdio_pcs = bus;
return 0;
out_pcs_free:
for (port = 0; port < ds->num_ports; port++) {
if (!priv->xpcs[port])
continue;
mdio_device_free(priv->xpcs[port]->mdiodev);
xpcs_destroy(priv->xpcs[port]);
priv->xpcs[port] = NULL;
}
mdiobus_unregister(bus);
mdiobus_free(bus);
return rc;
}
static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv)
{
struct dsa_switch *ds = priv->ds;
int port;
if (!priv->mdio_pcs)
return;
for (port = 0; port < ds->num_ports; port++) {
if (!priv->xpcs[port])
continue;
mdio_device_free(priv->xpcs[port]->mdiodev);
xpcs_destroy(priv->xpcs[port]);
priv->xpcs[port] = NULL;
}
mdiobus_unregister(priv->mdio_pcs);
mdiobus_free(priv->mdio_pcs);
priv->mdio_pcs = NULL;
}
int sja1105_mdiobus_register(struct dsa_switch *ds) int sja1105_mdiobus_register(struct dsa_switch *ds)
{ {
struct sja1105_private *priv = ds->priv; struct sja1105_private *priv = ds->priv;
...@@ -247,6 +496,10 @@ int sja1105_mdiobus_register(struct dsa_switch *ds) ...@@ -247,6 +496,10 @@ int sja1105_mdiobus_register(struct dsa_switch *ds)
struct device_node *mdio_node; struct device_node *mdio_node;
int rc; int rc;
rc = sja1105_mdiobus_pcs_register(priv);
if (rc)
return rc;
mdio_node = of_get_child_by_name(switch_node, "mdios"); mdio_node = of_get_child_by_name(switch_node, "mdios");
if (!mdio_node) if (!mdio_node)
return 0; return 0;
...@@ -275,6 +528,7 @@ int sja1105_mdiobus_register(struct dsa_switch *ds) ...@@ -275,6 +528,7 @@ int sja1105_mdiobus_register(struct dsa_switch *ds)
sja1105_mdiobus_base_tx_unregister(priv); sja1105_mdiobus_base_tx_unregister(priv);
err_put_mdio_node: err_put_mdio_node:
of_node_put(mdio_node); of_node_put(mdio_node);
sja1105_mdiobus_pcs_unregister(priv);
return rc; return rc;
} }
...@@ -285,4 +539,5 @@ void sja1105_mdiobus_unregister(struct dsa_switch *ds) ...@@ -285,4 +539,5 @@ void sja1105_mdiobus_unregister(struct dsa_switch *ds)
sja1105_mdiobus_base_t1_unregister(priv); sja1105_mdiobus_base_t1_unregister(priv);
sja1105_mdiobus_base_tx_unregister(priv); sja1105_mdiobus_base_tx_unregister(priv);
sja1105_mdiobus_pcs_unregister(priv);
} }
/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright 2020, NXP Semiconductors
*/
#ifndef _SJA1105_SGMII_H
#define _SJA1105_SGMII_H
#define SJA1105_SGMII_PORT 4
/* DIGITAL_CONTROL_1 (address 1f8000h) */
#define SJA1105_DC1 0x8000
#define SJA1105_DC1_VS_RESET BIT(15)
#define SJA1105_DC1_REMOTE_LOOPBACK BIT(14)
#define SJA1105_DC1_EN_VSMMD1 BIT(13)
#define SJA1105_DC1_POWER_SAVE BIT(11)
#define SJA1105_DC1_CLOCK_STOP_EN BIT(10)
#define SJA1105_DC1_MAC_AUTO_SW BIT(9)
#define SJA1105_DC1_INIT BIT(8)
#define SJA1105_DC1_TX_DISABLE BIT(4)
#define SJA1105_DC1_AUTONEG_TIMER_OVRR BIT(3)
#define SJA1105_DC1_BYP_POWERUP BIT(1)
#define SJA1105_DC1_PHY_MODE_CONTROL BIT(0)
/* DIGITAL_CONTROL_2 register (address 1f80E1h) */
#define SJA1105_DC2 0x80e1
#define SJA1105_DC2_TX_POL_INV_DISABLE BIT(4)
#define SJA1105_DC2_RX_POL_INV BIT(0)
/* DIGITAL_ERROR_CNT register (address 1f80E2h) */
#define SJA1105_DEC 0x80e2
#define SJA1105_DEC_ICG_EC_ENA BIT(4)
#define SJA1105_DEC_CLEAR_ON_READ BIT(0)
/* AUTONEG_CONTROL register (address 1f8001h) */
#define SJA1105_AC 0x8001
#define SJA1105_AC_MII_CONTROL BIT(8)
#define SJA1105_AC_SGMII_LINK BIT(4)
#define SJA1105_AC_PHY_MODE BIT(3)
#define SJA1105_AC_AUTONEG_MODE(x) (((x) << 1) & GENMASK(2, 1))
#define SJA1105_AC_AUTONEG_MODE_SGMII SJA1105_AC_AUTONEG_MODE(2)
/* AUTONEG_INTR_STATUS register (address 1f8002h) */
#define SJA1105_AIS 0x8002
#define SJA1105_AIS_LINK_STATUS(x) (!!((x) & BIT(4)))
#define SJA1105_AIS_SPEED(x) (((x) & GENMASK(3, 2)) >> 2)
#define SJA1105_AIS_DUPLEX_MODE(x) (!!((x) & BIT(1)))
#define SJA1105_AIS_COMPLETE(x) (!!((x) & BIT(0)))
/* DEBUG_CONTROL register (address 1f8005h) */
#define SJA1105_DC 0x8005
#define SJA1105_DC_SUPPRESS_LOS BIT(4)
#define SJA1105_DC_RESTART_SYNC BIT(0)
#endif
...@@ -561,6 +561,9 @@ static struct sja1105_regs sja1110_regs = { ...@@ -561,6 +561,9 @@ static struct sja1105_regs sja1110_regs = {
.ptpsyncts = SJA1110_SPI_ADDR(0x84), .ptpsyncts = SJA1110_SPI_ADDR(0x84),
.mdio_100base_tx = 0x1c2400, .mdio_100base_tx = 0x1c2400,
.mdio_100base_t1 = 0x1c1000, .mdio_100base_t1 = 0x1c1000,
.pcs_base = {SJA1105_RSV_ADDR, 0x1c1400, 0x1c1800, 0x1c1c00, 0x1c2000,
SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},
}; };
const struct sja1105_info sja1105e_info = { const struct sja1105_info sja1105e_info = {
...@@ -717,6 +720,8 @@ const struct sja1105_info sja1105r_info = { ...@@ -717,6 +720,8 @@ const struct sja1105_info sja1105r_info = {
.ptp_cmd_packing = sja1105pqrs_ptp_cmd_packing, .ptp_cmd_packing = sja1105pqrs_ptp_cmd_packing,
.rxtstamp = sja1105_rxtstamp, .rxtstamp = sja1105_rxtstamp,
.clocking_setup = sja1105_clocking_setup, .clocking_setup = sja1105_clocking_setup,
.pcs_mdio_read = sja1105_pcs_mdio_read,
.pcs_mdio_write = sja1105_pcs_mdio_write,
.regs = &sja1105pqrs_regs, .regs = &sja1105pqrs_regs,
.port_speed = { .port_speed = {
[SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_AUTO] = 0,
...@@ -753,6 +758,8 @@ const struct sja1105_info sja1105s_info = { ...@@ -753,6 +758,8 @@ const struct sja1105_info sja1105s_info = {
.ptp_cmd_packing = sja1105pqrs_ptp_cmd_packing, .ptp_cmd_packing = sja1105pqrs_ptp_cmd_packing,
.rxtstamp = sja1105_rxtstamp, .rxtstamp = sja1105_rxtstamp,
.clocking_setup = sja1105_clocking_setup, .clocking_setup = sja1105_clocking_setup,
.pcs_mdio_read = sja1105_pcs_mdio_read,
.pcs_mdio_write = sja1105_pcs_mdio_write,
.port_speed = { .port_speed = {
[SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 3, [SJA1105_SPEED_10MBPS] = 3,
...@@ -790,6 +797,8 @@ const struct sja1105_info sja1110a_info = { ...@@ -790,6 +797,8 @@ const struct sja1105_info sja1110a_info = {
.rxtstamp = sja1110_rxtstamp, .rxtstamp = sja1110_rxtstamp,
.txtstamp = sja1110_txtstamp, .txtstamp = sja1110_txtstamp,
.clocking_setup = sja1110_clocking_setup, .clocking_setup = sja1110_clocking_setup,
.pcs_mdio_read = sja1110_pcs_mdio_read,
.pcs_mdio_write = sja1110_pcs_mdio_write,
.port_speed = { .port_speed = {
[SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 4, [SJA1105_SPEED_10MBPS] = 4,
...@@ -839,6 +848,8 @@ const struct sja1105_info sja1110b_info = { ...@@ -839,6 +848,8 @@ const struct sja1105_info sja1110b_info = {
.rxtstamp = sja1110_rxtstamp, .rxtstamp = sja1110_rxtstamp,
.txtstamp = sja1110_txtstamp, .txtstamp = sja1110_txtstamp,
.clocking_setup = sja1110_clocking_setup, .clocking_setup = sja1110_clocking_setup,
.pcs_mdio_read = sja1110_pcs_mdio_read,
.pcs_mdio_write = sja1110_pcs_mdio_write,
.port_speed = { .port_speed = {
[SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 4, [SJA1105_SPEED_10MBPS] = 4,
...@@ -888,6 +899,8 @@ const struct sja1105_info sja1110c_info = { ...@@ -888,6 +899,8 @@ const struct sja1105_info sja1110c_info = {
.rxtstamp = sja1110_rxtstamp, .rxtstamp = sja1110_rxtstamp,
.txtstamp = sja1110_txtstamp, .txtstamp = sja1110_txtstamp,
.clocking_setup = sja1110_clocking_setup, .clocking_setup = sja1110_clocking_setup,
.pcs_mdio_read = sja1110_pcs_mdio_read,
.pcs_mdio_write = sja1110_pcs_mdio_write,
.port_speed = { .port_speed = {
[SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 4, [SJA1105_SPEED_10MBPS] = 4,
...@@ -937,6 +950,8 @@ const struct sja1105_info sja1110d_info = { ...@@ -937,6 +950,8 @@ const struct sja1105_info sja1110d_info = {
.rxtstamp = sja1110_rxtstamp, .rxtstamp = sja1110_rxtstamp,
.txtstamp = sja1110_txtstamp, .txtstamp = sja1110_txtstamp,
.clocking_setup = sja1110_clocking_setup, .clocking_setup = sja1110_clocking_setup,
.pcs_mdio_read = sja1110_pcs_mdio_read,
.pcs_mdio_write = sja1110_pcs_mdio_write,
.port_speed = { .port_speed = {
[SJA1105_SPEED_AUTO] = 0, [SJA1105_SPEED_AUTO] = 0,
[SJA1105_SPEED_10MBPS] = 4, [SJA1105_SPEED_10MBPS] = 4,
...@@ -952,6 +967,8 @@ const struct sja1105_info sja1110d_info = { ...@@ -952,6 +967,8 @@ const struct sja1105_info sja1110d_info = {
false, false, false, false, false, false}, false, false, false, false, false, false},
.supports_sgmii = {false, true, true, true, true, .supports_sgmii = {false, true, true, true, true,
false, false, false, false, false, false}, false, false, false, false, false, false},
.supports_2500basex = {false, false, false, true, true,
false, false, false, false, false, false},
.internal_phy = {SJA1105_NO_PHY, SJA1105_NO_PHY, .internal_phy = {SJA1105_NO_PHY, SJA1105_NO_PHY,
SJA1105_NO_PHY, SJA1105_NO_PHY, SJA1105_NO_PHY, SJA1105_NO_PHY,
SJA1105_NO_PHY, SJA1105_PHY_BASE_T1, SJA1105_NO_PHY, SJA1105_PHY_BASE_T1,
......
...@@ -503,7 +503,7 @@ struct mac_device_info { ...@@ -503,7 +503,7 @@ struct mac_device_info {
const struct stmmac_hwtimestamp *ptp; const struct stmmac_hwtimestamp *ptp;
const struct stmmac_tc_ops *tc; const struct stmmac_tc_ops *tc;
const struct stmmac_mmc_ops *mmc; const struct stmmac_mmc_ops *mmc;
struct mdio_xpcs_args *xpcs; struct dw_xpcs *xpcs;
struct mii_regs mii; /* MII register Addresses */ struct mii_regs mii; /* MII register Addresses */
struct mac_link link; struct mac_link link;
void __iomem *pcsr; /* vpointer to device CSRs */ void __iomem *pcsr; /* vpointer to device CSRs */
......
...@@ -7003,12 +7003,10 @@ int stmmac_dvr_probe(struct device *device, ...@@ -7003,12 +7003,10 @@ int stmmac_dvr_probe(struct device *device,
if (priv->plat->speed_mode_2500) if (priv->plat->speed_mode_2500)
priv->plat->speed_mode_2500(ndev, priv->plat->bsp_priv); priv->plat->speed_mode_2500(ndev, priv->plat->bsp_priv);
if (priv->plat->mdio_bus_data) { if (priv->plat->mdio_bus_data && priv->plat->mdio_bus_data->has_xpcs) {
if (priv->plat->mdio_bus_data->has_xpcs) { ret = stmmac_xpcs_setup(priv->mii);
ret = stmmac_xpcs_setup(priv->mii); if (ret)
if (ret) goto error_xpcs_setup;
goto error_xpcs_setup;
}
} }
ret = stmmac_phy_setup(priv); ret = stmmac_phy_setup(priv);
......
...@@ -399,11 +399,11 @@ int stmmac_mdio_reset(struct mii_bus *bus) ...@@ -399,11 +399,11 @@ int stmmac_mdio_reset(struct mii_bus *bus)
int stmmac_xpcs_setup(struct mii_bus *bus) int stmmac_xpcs_setup(struct mii_bus *bus)
{ {
int mode, addr;
struct net_device *ndev = bus->priv; struct net_device *ndev = bus->priv;
struct mdio_xpcs_args *xpcs;
struct stmmac_priv *priv;
struct mdio_device *mdiodev; struct mdio_device *mdiodev;
struct stmmac_priv *priv;
struct dw_xpcs *xpcs;
int mode, addr;
priv = netdev_priv(ndev); priv = netdev_priv(ndev);
mode = priv->plat->phy_interface; mode = priv->plat->phy_interface;
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
# Makefile for Linux PCS drivers # Makefile for Linux PCS drivers
obj-$(CONFIG_PCS_XPCS) += pcs-xpcs.o pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o
obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2021 NXP Semiconductors
*/
#include <linux/pcs/pcs-xpcs.h>
#include "pcs-xpcs.h"
/* LANE_DRIVER1_0 register */
#define SJA1110_LANE_DRIVER1_0 0x8038
#define SJA1110_TXDRV(x) (((x) << 12) & GENMASK(14, 12))
/* LANE_DRIVER2_0 register */
#define SJA1110_LANE_DRIVER2_0 0x803a
#define SJA1110_TXDRVTRIM_LSB(x) ((x) & GENMASK_ULL(15, 0))
/* LANE_DRIVER2_1 register */
#define SJA1110_LANE_DRIVER2_1 0x803b
#define SJA1110_LANE_DRIVER2_1_RSV BIT(9)
#define SJA1110_TXDRVTRIM_MSB(x) (((x) & GENMASK_ULL(23, 16)) >> 16)
/* LANE_TRIM register */
#define SJA1110_LANE_TRIM 0x8040
#define SJA1110_TXTEN BIT(11)
#define SJA1110_TXRTRIM(x) (((x) << 8) & GENMASK(10, 8))
#define SJA1110_TXPLL_BWSEL BIT(7)
#define SJA1110_RXTEN BIT(6)
#define SJA1110_RXRTRIM(x) (((x) << 3) & GENMASK(5, 3))
#define SJA1110_CDR_GAIN BIT(2)
#define SJA1110_ACCOUPLE_RXVCM_EN BIT(0)
/* LANE_DATAPATH_1 register */
#define SJA1110_LANE_DATAPATH_1 0x8037
/* POWERDOWN_ENABLE register */
#define SJA1110_POWERDOWN_ENABLE 0x8041
#define SJA1110_TXPLL_PD BIT(12)
#define SJA1110_TXPD BIT(11)
#define SJA1110_RXPKDETEN BIT(10)
#define SJA1110_RXCH_PD BIT(9)
#define SJA1110_RXBIAS_PD BIT(8)
#define SJA1110_RESET_SER_EN BIT(7)
#define SJA1110_RESET_SER BIT(6)
#define SJA1110_RESET_DES BIT(5)
#define SJA1110_RCVEN BIT(4)
/* RXPLL_CTRL0 register */
#define SJA1110_RXPLL_CTRL0 0x8065
#define SJA1110_RXPLL_FBDIV(x) (((x) << 2) & GENMASK(9, 2))
/* RXPLL_CTRL1 register */
#define SJA1110_RXPLL_CTRL1 0x8066
#define SJA1110_RXPLL_REFDIV(x) ((x) & GENMASK(4, 0))
/* TXPLL_CTRL0 register */
#define SJA1110_TXPLL_CTRL0 0x806d
#define SJA1110_TXPLL_FBDIV(x) ((x) & GENMASK(11, 0))
/* TXPLL_CTRL1 register */
#define SJA1110_TXPLL_CTRL1 0x806e
#define SJA1110_TXPLL_REFDIV(x) ((x) & GENMASK(5, 0))
/* RX_DATA_DETECT register */
#define SJA1110_RX_DATA_DETECT 0x8045
/* RX_CDR_CTLE register */
#define SJA1110_RX_CDR_CTLE 0x8042
/* In NXP SJA1105, the PCS is integrated with a PMA that has the TX lane
* polarity inverted by default (PLUS is MINUS, MINUS is PLUS). To obtain
* normal non-inverted behavior, the TX lane polarity must be inverted in the
* PCS, via the DIGITAL_CONTROL_2 register.
*/
int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs)
{
return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL2,
DW_VR_MII_DIG_CTRL2_TX_POL_INV);
}
static int nxp_sja1110_pma_config(struct dw_xpcs *xpcs,
u16 txpll_fbdiv, u16 txpll_refdiv,
u16 rxpll_fbdiv, u16 rxpll_refdiv,
u16 rx_cdr_ctle)
{
u16 val;
int ret;
/* Program TX PLL feedback divider and reference divider settings for
* correct oscillation frequency.
*/
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_TXPLL_CTRL0,
SJA1110_TXPLL_FBDIV(txpll_fbdiv));
if (ret < 0)
return ret;
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_TXPLL_CTRL1,
SJA1110_TXPLL_REFDIV(txpll_refdiv));
if (ret < 0)
return ret;
/* Program transmitter amplitude and disable amplitude trimming */
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_DRIVER1_0,
SJA1110_TXDRV(0x5));
if (ret < 0)
return ret;
val = SJA1110_TXDRVTRIM_LSB(0xffffffull);
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_DRIVER2_0, val);
if (ret < 0)
return ret;
val = SJA1110_TXDRVTRIM_MSB(0xffffffull) | SJA1110_LANE_DRIVER2_1_RSV;
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_DRIVER2_1, val);
if (ret < 0)
return ret;
/* Enable input and output resistor terminations for low BER. */
val = SJA1110_ACCOUPLE_RXVCM_EN | SJA1110_CDR_GAIN |
SJA1110_RXRTRIM(4) | SJA1110_RXTEN | SJA1110_TXPLL_BWSEL |
SJA1110_TXRTRIM(3) | SJA1110_TXTEN;
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_TRIM, val);
if (ret < 0)
return ret;
/* Select PCS as transmitter data source. */
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_DATAPATH_1, 0);
if (ret < 0)
return ret;
/* Program RX PLL feedback divider and reference divider for correct
* oscillation frequency.
*/
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RXPLL_CTRL0,
SJA1110_RXPLL_FBDIV(rxpll_fbdiv));
if (ret < 0)
return ret;
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RXPLL_CTRL1,
SJA1110_RXPLL_REFDIV(rxpll_refdiv));
if (ret < 0)
return ret;
/* Program threshold for receiver signal detector.
* Enable control of RXPLL by receiver signal detector to disable RXPLL
* when an input signal is not present.
*/
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RX_DATA_DETECT, 0x0005);
if (ret < 0)
return ret;
/* Enable TX and RX PLLs and circuits.
* Release reset of PMA to enable data flow to/from PCS.
*/
val = xpcs_read(xpcs, MDIO_MMD_VEND2, SJA1110_POWERDOWN_ENABLE);
if (val < 0)
return val;
val &= ~(SJA1110_TXPLL_PD | SJA1110_TXPD | SJA1110_RXCH_PD |
SJA1110_RXBIAS_PD | SJA1110_RESET_SER_EN |
SJA1110_RESET_SER | SJA1110_RESET_DES);
val |= SJA1110_RXPKDETEN | SJA1110_RCVEN;
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_POWERDOWN_ENABLE, val);
if (ret < 0)
return ret;
/* Program continuous-time linear equalizer (CTLE) settings. */
ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RX_CDR_CTLE,
rx_cdr_ctle);
if (ret < 0)
return ret;
return 0;
}
int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs)
{
return nxp_sja1110_pma_config(xpcs, 0x19, 0x1, 0x19, 0x1, 0x212a);
}
int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs)
{
return nxp_sja1110_pma_config(xpcs, 0x7d, 0x2, 0x7d, 0x2, 0x732a);
}
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020 Synopsys, Inc. and/or its affiliates.
* Synopsys DesignWare XPCS helpers
*
* Author: Jose Abreu <Jose.Abreu@synopsys.com>
*/
#define SYNOPSYS_XPCS_ID 0x7996ced0
#define SYNOPSYS_XPCS_MASK 0xffffffff
/* Vendor regs access */
#define DW_VENDOR BIT(15)
/* VR_XS_PCS */
#define DW_USXGMII_RST BIT(10)
#define DW_USXGMII_EN BIT(9)
#define DW_VR_XS_PCS_DIG_STS 0x0010
#define DW_RXFIFO_ERR GENMASK(6, 5)
/* SR_MII */
#define DW_USXGMII_FULL BIT(8)
#define DW_USXGMII_SS_MASK (BIT(13) | BIT(6) | BIT(5))
#define DW_USXGMII_10000 (BIT(13) | BIT(6))
#define DW_USXGMII_5000 (BIT(13) | BIT(5))
#define DW_USXGMII_2500 (BIT(5))
#define DW_USXGMII_1000 (BIT(6))
#define DW_USXGMII_100 (BIT(13))
#define DW_USXGMII_10 (0)
/* SR_AN */
#define DW_SR_AN_ADV1 0x10
#define DW_SR_AN_ADV2 0x11
#define DW_SR_AN_ADV3 0x12
#define DW_SR_AN_LP_ABL1 0x13
#define DW_SR_AN_LP_ABL2 0x14
#define DW_SR_AN_LP_ABL3 0x15
/* Clause 73 Defines */
/* AN_LP_ABL1 */
#define DW_C73_PAUSE BIT(10)
#define DW_C73_ASYM_PAUSE BIT(11)
#define DW_C73_AN_ADV_SF 0x1
/* AN_LP_ABL2 */
#define DW_C73_1000KX BIT(5)
#define DW_C73_10000KX4 BIT(6)
#define DW_C73_10000KR BIT(7)
/* AN_LP_ABL3 */
#define DW_C73_2500KX BIT(0)
#define DW_C73_5000KR BIT(1)
/* Clause 37 Defines */
/* VR MII MMD registers offsets */
#define DW_VR_MII_MMD_CTRL 0x0000
#define DW_VR_MII_DIG_CTRL1 0x8000
#define DW_VR_MII_AN_CTRL 0x8001
#define DW_VR_MII_AN_INTR_STS 0x8002
/* Enable 2.5G Mode */
#define DW_VR_MII_DIG_CTRL1_2G5_EN BIT(2)
/* EEE Mode Control Register */
#define DW_VR_MII_EEE_MCTRL0 0x8006
#define DW_VR_MII_EEE_MCTRL1 0x800b
#define DW_VR_MII_DIG_CTRL2 0x80e1
/* VR_MII_DIG_CTRL1 */
#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9)
/* VR_MII_DIG_CTRL2 */
#define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4)
#define DW_VR_MII_DIG_CTRL2_RX_POL_INV BIT(0)
/* VR_MII_AN_CTRL */
#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3
#define DW_VR_MII_TX_CONFIG_MASK BIT(3)
#define DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII 0x1
#define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0
#define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT 1
#define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1)
#define DW_VR_MII_PCS_MODE_C37_1000BASEX 0x0
#define DW_VR_MII_PCS_MODE_C37_SGMII 0x2
/* VR_MII_AN_INTR_STS */
#define DW_VR_MII_AN_STS_C37_ANSGM_FD BIT(1)
#define DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT 2
#define DW_VR_MII_AN_STS_C37_ANSGM_SP GENMASK(3, 2)
#define DW_VR_MII_C37_ANSGM_SP_10 0x0
#define DW_VR_MII_C37_ANSGM_SP_100 0x1
#define DW_VR_MII_C37_ANSGM_SP_1000 0x2
#define DW_VR_MII_C37_ANSGM_SP_LNKSTS BIT(4)
/* SR MII MMD Control defines */
#define AN_CL37_EN BIT(12) /* Enable Clause 37 auto-nego */
#define SGMII_SPEED_SS13 BIT(13) /* SGMII speed along with SS6 */
#define SGMII_SPEED_SS6 BIT(6) /* SGMII speed along with SS13 */
/* VR MII EEE Control 0 defines */
#define DW_VR_MII_EEE_LTX_EN BIT(0) /* LPI Tx Enable */
#define DW_VR_MII_EEE_LRX_EN BIT(1) /* LPI Rx Enable */
#define DW_VR_MII_EEE_TX_QUIET_EN BIT(2) /* Tx Quiet Enable */
#define DW_VR_MII_EEE_RX_QUIET_EN BIT(3) /* Rx Quiet Enable */
#define DW_VR_MII_EEE_TX_EN_CTRL BIT(4) /* Tx Control Enable */
#define DW_VR_MII_EEE_RX_EN_CTRL BIT(7) /* Rx Control Enable */
#define DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT 8
#define DW_VR_MII_EEE_MULT_FACT_100NS GENMASK(11, 8)
/* VR MII EEE Control 1 defines */
#define DW_VR_MII_EEE_TRN_LPI BIT(0) /* Transparent Mode Enable */
int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg);
int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val);
int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs);
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/phylink.h> #include <linux/phylink.h>
#define NXP_SJA1105_XPCS_ID 0x00000010
#define NXP_SJA1110_XPCS_ID 0x00000020
/* AN mode */ /* AN mode */
#define DW_AN_C73 1 #define DW_AN_C73 1
#define DW_AN_C37_SGMII 2 #define DW_AN_C37_SGMII 2
...@@ -17,19 +20,23 @@ ...@@ -17,19 +20,23 @@
struct xpcs_id; struct xpcs_id;
struct mdio_xpcs_args { struct dw_xpcs {
struct mdio_device *mdiodev; struct mdio_device *mdiodev;
const struct xpcs_id *id; const struct xpcs_id *id;
struct phylink_pcs pcs; struct phylink_pcs pcs;
}; };
int xpcs_get_an_mode(struct mdio_xpcs_args *xpcs, phy_interface_t interface); int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
void xpcs_validate(struct mdio_xpcs_args *xpcs, unsigned long *supported, void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
phy_interface_t interface, int speed, int duplex);
int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
unsigned int mode);
void xpcs_validate(struct dw_xpcs *xpcs, unsigned long *supported,
struct phylink_link_state *state); struct phylink_link_state *state);
int xpcs_config_eee(struct mdio_xpcs_args *xpcs, int mult_fact_100ns, int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns,
int enable); int enable);
struct mdio_xpcs_args *xpcs_create(struct mdio_device *mdiodev, struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
phy_interface_t interface); phy_interface_t interface);
void xpcs_destroy(struct mdio_xpcs_args *xpcs); void xpcs_destroy(struct dw_xpcs *xpcs);
#endif /* __LINUX_PCS_XPCS_H */ #endif /* __LINUX_PCS_XPCS_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