Message ID | 20200320112937.27203-1-olteanv@gmail.com |
---|---|
State | Accepted |
Delegated to: | David Miller |
Headers | show |
Series | [v3,net-next] net: dsa: sja1105: Add support for the SGMII port | expand |
On Fri, Mar 20, 2020 at 01:29:37PM +0200, Vladimir Oltean wrote: > From: Vladimir Oltean <vladimir.oltean@nxp.com> > > SJA1105 switches R and S have one SerDes port with an 802.3z > quasi-compatible PCS, hardwired on port 4. The other ports are still > MII/RMII/RGMII. The PCS performs rate adaptation to lower link speeds; > the MAC on this port is hardwired at gigabit. Only full duplex is > supported. > > The SGMII port can be configured as part of the static config tables, as > well as through a dedicated SPI address region for its pseudo-clause-22 > registers. However it looks like the static configuration is not > able to change some out-of-reset values (like the value of MII_BMCR), so > at the end of the day, having code for it is utterly pointless. We are > just going to use the pseudo-C22 interface. > > Because the PCS gets reset when the switch resets, we have to add even > more restoration logic to sja1105_static_config_reload, otherwise the > SGMII port breaks after operations such as enabling PTP timestamping > which require a switch reset. > > From PHYLINK perspective, the switch supports *only* SGMII (it doesn't > support 1000Base-X). It also doesn't expose access to the raw config > word for in-band AN in registers MII_ADV/MII_LPA. > It is able to work in the following modes: > - Forced speed > - SGMII in-band AN slave (speed received from PHY) > - SGMII in-band AN master (acting as a PHY) > > The latter mode is not supported by this patch. It is even unclear to me > how that would be described. There is some code for it left in the > patch, but 'an_master' is always passed as false. > > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Looks good from the phylink perspective now, thanks. Reviewed-by: Russell King <rmk+kernel@armlinux.org.uk> > --- > Changes in v3: > - Separated sja1105_sgmii_config into: > * sja1105_sgmii_pcs_config for the invariant configuration (PCS > bring-up and optional enabling of in-band AN). This is called from > .phylink_mac_config > * sja1105_sgmii_pcs_force_speed, which is for non-inband modes and is > called from .phylink_mac_link_up. > - Adapted the restoration logic to call both functions if necessary. > - Added back support for in-band AN and the .phylink_pcs_get_state > function. > - Using the phylink_autoneg_inband helper function now. > > Changes in v2: > - Moved PCS forced speed to .phylink_mac_link_up > - Removed not-fully-thought-out code for in-band AN slave > > drivers/net/dsa/sja1105/sja1105.h | 2 + > drivers/net/dsa/sja1105/sja1105_clocking.c | 4 + > drivers/net/dsa/sja1105/sja1105_main.c | 189 ++++++++++++++++++++- > drivers/net/dsa/sja1105/sja1105_sgmii.h | 53 ++++++ > drivers/net/dsa/sja1105/sja1105_spi.c | 1 + > 5 files changed, 244 insertions(+), 5 deletions(-) > create mode 100644 drivers/net/dsa/sja1105/sja1105_sgmii.h > > diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h > index d801fc204d19..4c40f2d51a54 100644 > --- a/drivers/net/dsa/sja1105/sja1105.h > +++ b/drivers/net/dsa/sja1105/sja1105.h > @@ -36,6 +36,7 @@ struct sja1105_regs { > u64 port_control; > u64 rgu; > u64 config; > + u64 sgmii; > u64 rmii_pll1; > u64 ptp_control; > u64 ptpclkval; > @@ -159,6 +160,7 @@ typedef enum { > XMII_MODE_MII = 0, > XMII_MODE_RMII = 1, > XMII_MODE_RGMII = 2, > + XMII_MODE_SGMII = 3, > } sja1105_phy_interface_t; > > typedef enum { > diff --git a/drivers/net/dsa/sja1105/sja1105_clocking.c b/drivers/net/dsa/sja1105/sja1105_clocking.c > index 9082e52b55e9..0fdc2d55fff6 100644 > --- a/drivers/net/dsa/sja1105/sja1105_clocking.c > +++ b/drivers/net/dsa/sja1105/sja1105_clocking.c > @@ -660,6 +660,10 @@ int sja1105_clocking_setup_port(struct sja1105_private *priv, int port) > case XMII_MODE_RGMII: > rc = sja1105_rgmii_clocking_setup(priv, port, role); > break; > + case XMII_MODE_SGMII: > + /* Nothing to do in the CGU for SGMII */ > + rc = 0; > + break; > default: > dev_err(dev, "Invalid interface mode specified: %d\n", > phy_mode); > diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c > index edf57ea07083..afafe2ecf248 100644 > --- a/drivers/net/dsa/sja1105/sja1105_main.c > +++ b/drivers/net/dsa/sja1105/sja1105_main.c > @@ -22,6 +22,7 @@ > #include <linux/if_ether.h> > #include <linux/dsa/8021q.h> > #include "sja1105.h" > +#include "sja1105_sgmii.h" > #include "sja1105_tas.h" > > static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len, > @@ -135,6 +136,21 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) > return 0; > } > > +static bool sja1105_supports_sgmii(struct sja1105_private *priv, int port) > +{ > + if (priv->info->part_no != SJA1105R_PART_NO && > + priv->info->part_no != SJA1105S_PART_NO) > + return false; > + > + if (port != SJA1105_SGMII_PORT) > + return false; > + > + if (dsa_is_unused_port(priv->ds, port)) > + return false; > + > + return true; > +} > + > static int sja1105_init_mii_settings(struct sja1105_private *priv, > struct sja1105_dt_port *ports) > { > @@ -178,12 +194,24 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv, > case PHY_INTERFACE_MODE_RGMII_TXID: > mii->xmii_mode[i] = XMII_MODE_RGMII; > break; > + case PHY_INTERFACE_MODE_SGMII: > + if (!sja1105_supports_sgmii(priv, i)) > + return -EINVAL; > + mii->xmii_mode[i] = XMII_MODE_SGMII; > + break; > default: > dev_err(dev, "Unsupported PHY mode %s!\n", > phy_modes(ports[i].phy_mode)); > } > > - mii->phy_mac[i] = ports[i].role; > + /* Even though the SerDes port is able to drive SGMII autoneg > + * like a PHY would, from the perspective of the XMII tables, > + * the SGMII port should always be put in MAC mode. > + */ > + if (ports[i].phy_mode == PHY_INTERFACE_MODE_SGMII) > + mii->phy_mac[i] = XMII_MAC; > + else > + mii->phy_mac[i] = ports[i].role; > } > return 0; > } > @@ -650,6 +678,85 @@ static int sja1105_parse_dt(struct sja1105_private *priv, > return rc; > } > > +static int sja1105_sgmii_read(struct sja1105_private *priv, int pcs_reg) > +{ > + const struct sja1105_regs *regs = priv->info->regs; > + u32 val; > + int rc; > + > + rc = sja1105_xfer_u32(priv, SPI_READ, regs->sgmii + pcs_reg, &val, > + NULL); > + if (rc < 0) > + return rc; > + > + return val; > +} > + > +static int sja1105_sgmii_write(struct sja1105_private *priv, int pcs_reg, > + u16 pcs_val) > +{ > + const struct sja1105_regs *regs = priv->info->regs; > + u32 val = pcs_val; > + int rc; > + > + rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->sgmii + pcs_reg, &val, > + NULL); > + if (rc < 0) > + return rc; > + > + return val; > +} > + > +static void sja1105_sgmii_pcs_config(struct sja1105_private *priv, > + 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, 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, 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, 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, MII_BMCR, > + BMCR_ANENABLE | BMCR_ANRESTART); > +} > + > +static void sja1105_sgmii_pcs_force_speed(struct sja1105_private *priv, > + 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, MII_BMCR, pcs_speed | BMCR_FULLDPLX); > +} > + > /* Convert link speed from SJA1105 to ethtool encoding */ > static int sja1105_speed[] = { > [SJA1105_SPEED_AUTO] = SPEED_UNKNOWN, > @@ -707,8 +814,13 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, > * table, since this will be used for the clocking setup, and we no > * longer need to store it in the static config (already told hardware > * we want auto during upload phase). > + * Actually for the SGMII port, the MAC is fixed at 1 Gbps and > + * we need to configure the PCS only (if even that). > */ > - mac[port].speed = speed; > + if (sja1105_supports_sgmii(priv, port)) > + mac[port].speed = SJA1105_SPEED_1000MBPS; > + else > + mac[port].speed = speed; > > /* Write to the dynamic reconfiguration tables */ > rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, > @@ -757,16 +869,19 @@ static bool sja1105_phy_mode_mismatch(struct sja1105_private *priv, int port, > case PHY_INTERFACE_MODE_RGMII_RXID: > case PHY_INTERFACE_MODE_RGMII_TXID: > return (phy_mode != XMII_MODE_RGMII); > + case PHY_INTERFACE_MODE_SGMII: > + return (phy_mode != XMII_MODE_SGMII); > default: > return true; > } > } > > static void sja1105_mac_config(struct dsa_switch *ds, int port, > - unsigned int link_an_mode, > + unsigned int mode, > const struct phylink_link_state *state) > { > struct sja1105_private *priv = ds->priv; > + bool is_sgmii = sja1105_supports_sgmii(priv, port); > > if (sja1105_phy_mode_mismatch(priv, port, state->interface)) { > dev_err(ds->dev, "Changing PHY mode to %s not supported!\n", > @@ -774,10 +889,14 @@ static void sja1105_mac_config(struct dsa_switch *ds, int port, > return; > } > > - if (link_an_mode == MLO_AN_INBAND) { > + if (phylink_autoneg_inband(mode) && !is_sgmii) { > dev_err(ds->dev, "In-band AN not supported!\n"); > return; > } > + > + if (is_sgmii) > + sja1105_sgmii_pcs_config(priv, phylink_autoneg_inband(mode), > + false); > } > > static void sja1105_mac_link_down(struct dsa_switch *ds, int port, > @@ -798,6 +917,9 @@ static void sja1105_mac_link_up(struct dsa_switch *ds, int port, > > sja1105_adjust_port_config(priv, port, speed); > > + if (sja1105_supports_sgmii(priv, port) && !phylink_autoneg_inband(mode)) > + sja1105_sgmii_pcs_force_speed(priv, speed); > + > sja1105_inhibit_tx(priv, BIT(port), false); > } > > @@ -833,7 +955,8 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port, > phylink_set(mask, 10baseT_Full); > phylink_set(mask, 100baseT_Full); > phylink_set(mask, 100baseT1_Full); > - if (mii->xmii_mode[port] == XMII_MODE_RGMII) > + if (mii->xmii_mode[port] == XMII_MODE_RGMII || > + mii->xmii_mode[port] == XMII_MODE_SGMII) > phylink_set(mask, 1000baseT_Full); > > bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); > @@ -841,6 +964,38 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port, > __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, 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 > sja1105_find_static_fdb_entry(struct sja1105_private *priv, int port, > const struct sja1105_l2_lookup_entry *requested) > @@ -1367,6 +1522,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv, > struct dsa_switch *ds = priv->ds; > s64 t1, t2, t3, t4; > s64 t12, t34; > + u16 bmcr = 0; > int rc, i; > s64 now; > > @@ -1384,6 +1540,9 @@ int sja1105_static_config_reload(struct sja1105_private *priv, > mac[i].speed = SJA1105_SPEED_AUTO; > } > > + if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) > + bmcr = sja1105_sgmii_read(priv, MII_BMCR); > + > /* No PTP operations can run right now */ > mutex_lock(&priv->ptp_data.lock); > > @@ -1433,6 +1592,25 @@ int sja1105_static_config_reload(struct sja1105_private *priv, > if (rc < 0) > goto out; > } > + > + if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) { > + bool an_enabled = !!(bmcr & BMCR_ANENABLE); > + > + sja1105_sgmii_pcs_config(priv, an_enabled, false); > + > + if (!an_enabled) { > + int speed = SPEED_UNKNOWN; > + > + if (bmcr & BMCR_SPEED1000) > + speed = SPEED_1000; > + else if (bmcr & BMCR_SPEED100) > + speed = SPEED_100; > + else if (bmcr & BMCR_SPEED10) > + speed = SPEED_10; > + > + sja1105_sgmii_pcs_force_speed(priv, speed); > + } > + } > out: > mutex_unlock(&priv->mgmt_lock); > > @@ -1998,6 +2176,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = { > .teardown = sja1105_teardown, > .set_ageing_time = sja1105_set_ageing_time, > .phylink_validate = sja1105_phylink_validate, > + .phylink_mac_link_state = sja1105_mac_pcs_get_state, > .phylink_mac_config = sja1105_mac_config, > .phylink_mac_link_up = sja1105_mac_link_up, > .phylink_mac_link_down = sja1105_mac_link_down, > diff --git a/drivers/net/dsa/sja1105/sja1105_sgmii.h b/drivers/net/dsa/sja1105/sja1105_sgmii.h > new file mode 100644 > index 000000000000..24d9bc046e70 > --- /dev/null > +++ b/drivers/net/dsa/sja1105/sja1105_sgmii.h > @@ -0,0 +1,53 @@ > +/* 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 > diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c > index 29b127f3bf9c..45da162ba268 100644 > --- a/drivers/net/dsa/sja1105/sja1105_spi.c > +++ b/drivers/net/dsa/sja1105/sja1105_spi.c > @@ -474,6 +474,7 @@ static struct sja1105_regs sja1105pqrs_regs = { > /* UM10944.pdf, Table 86, ACU Register overview */ > .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808}, > .pad_mii_id = {0x100810, 0x100811, 0x100812, 0x100813, 0x100814}, > + .sgmii = 0x1F0000, > .rmii_pll1 = 0x10000A, > .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F}, > .mac = {0x200, 0x202, 0x204, 0x206, 0x208}, > -- > 2.17.1 > >
From: Vladimir Oltean <olteanv@gmail.com> Date: Fri, 20 Mar 2020 13:29:37 +0200 > From: Vladimir Oltean <vladimir.oltean@nxp.com> > > SJA1105 switches R and S have one SerDes port with an 802.3z > quasi-compatible PCS, hardwired on port 4. The other ports are still > MII/RMII/RGMII. The PCS performs rate adaptation to lower link speeds; > the MAC on this port is hardwired at gigabit. Only full duplex is > supported. > > The SGMII port can be configured as part of the static config tables, as > well as through a dedicated SPI address region for its pseudo-clause-22 > registers. However it looks like the static configuration is not > able to change some out-of-reset values (like the value of MII_BMCR), so > at the end of the day, having code for it is utterly pointless. We are > just going to use the pseudo-C22 interface. > > Because the PCS gets reset when the switch resets, we have to add even > more restoration logic to sja1105_static_config_reload, otherwise the > SGMII port breaks after operations such as enabling PTP timestamping > which require a switch reset. > > From PHYLINK perspective, the switch supports *only* SGMII (it doesn't > support 1000Base-X). It also doesn't expose access to the raw config > word for in-band AN in registers MII_ADV/MII_LPA. > It is able to work in the following modes: > - Forced speed > - SGMII in-band AN slave (speed received from PHY) > - SGMII in-band AN master (acting as a PHY) > > The latter mode is not supported by this patch. It is even unclear to me > how that would be described. There is some code for it left in the > patch, but 'an_master' is always passed as false. > > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Applied, thank you.
diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index d801fc204d19..4c40f2d51a54 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -36,6 +36,7 @@ struct sja1105_regs { u64 port_control; u64 rgu; u64 config; + u64 sgmii; u64 rmii_pll1; u64 ptp_control; u64 ptpclkval; @@ -159,6 +160,7 @@ typedef enum { XMII_MODE_MII = 0, XMII_MODE_RMII = 1, XMII_MODE_RGMII = 2, + XMII_MODE_SGMII = 3, } sja1105_phy_interface_t; typedef enum { diff --git a/drivers/net/dsa/sja1105/sja1105_clocking.c b/drivers/net/dsa/sja1105/sja1105_clocking.c index 9082e52b55e9..0fdc2d55fff6 100644 --- a/drivers/net/dsa/sja1105/sja1105_clocking.c +++ b/drivers/net/dsa/sja1105/sja1105_clocking.c @@ -660,6 +660,10 @@ int sja1105_clocking_setup_port(struct sja1105_private *priv, int port) case XMII_MODE_RGMII: rc = sja1105_rgmii_clocking_setup(priv, port, role); break; + case XMII_MODE_SGMII: + /* Nothing to do in the CGU for SGMII */ + rc = 0; + break; default: dev_err(dev, "Invalid interface mode specified: %d\n", phy_mode); diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index edf57ea07083..afafe2ecf248 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -22,6 +22,7 @@ #include <linux/if_ether.h> #include <linux/dsa/8021q.h> #include "sja1105.h" +#include "sja1105_sgmii.h" #include "sja1105_tas.h" static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len, @@ -135,6 +136,21 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) return 0; } +static bool sja1105_supports_sgmii(struct sja1105_private *priv, int port) +{ + if (priv->info->part_no != SJA1105R_PART_NO && + priv->info->part_no != SJA1105S_PART_NO) + return false; + + if (port != SJA1105_SGMII_PORT) + return false; + + if (dsa_is_unused_port(priv->ds, port)) + return false; + + return true; +} + static int sja1105_init_mii_settings(struct sja1105_private *priv, struct sja1105_dt_port *ports) { @@ -178,12 +194,24 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv, case PHY_INTERFACE_MODE_RGMII_TXID: mii->xmii_mode[i] = XMII_MODE_RGMII; break; + case PHY_INTERFACE_MODE_SGMII: + if (!sja1105_supports_sgmii(priv, i)) + return -EINVAL; + mii->xmii_mode[i] = XMII_MODE_SGMII; + break; default: dev_err(dev, "Unsupported PHY mode %s!\n", phy_modes(ports[i].phy_mode)); } - mii->phy_mac[i] = ports[i].role; + /* Even though the SerDes port is able to drive SGMII autoneg + * like a PHY would, from the perspective of the XMII tables, + * the SGMII port should always be put in MAC mode. + */ + if (ports[i].phy_mode == PHY_INTERFACE_MODE_SGMII) + mii->phy_mac[i] = XMII_MAC; + else + mii->phy_mac[i] = ports[i].role; } return 0; } @@ -650,6 +678,85 @@ static int sja1105_parse_dt(struct sja1105_private *priv, return rc; } +static int sja1105_sgmii_read(struct sja1105_private *priv, int pcs_reg) +{ + const struct sja1105_regs *regs = priv->info->regs; + u32 val; + int rc; + + rc = sja1105_xfer_u32(priv, SPI_READ, regs->sgmii + pcs_reg, &val, + NULL); + if (rc < 0) + return rc; + + return val; +} + +static int sja1105_sgmii_write(struct sja1105_private *priv, int pcs_reg, + u16 pcs_val) +{ + const struct sja1105_regs *regs = priv->info->regs; + u32 val = pcs_val; + int rc; + + rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->sgmii + pcs_reg, &val, + NULL); + if (rc < 0) + return rc; + + return val; +} + +static void sja1105_sgmii_pcs_config(struct sja1105_private *priv, + 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, 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, 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, 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, MII_BMCR, + BMCR_ANENABLE | BMCR_ANRESTART); +} + +static void sja1105_sgmii_pcs_force_speed(struct sja1105_private *priv, + 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, MII_BMCR, pcs_speed | BMCR_FULLDPLX); +} + /* Convert link speed from SJA1105 to ethtool encoding */ static int sja1105_speed[] = { [SJA1105_SPEED_AUTO] = SPEED_UNKNOWN, @@ -707,8 +814,13 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, * table, since this will be used for the clocking setup, and we no * longer need to store it in the static config (already told hardware * we want auto during upload phase). + * Actually for the SGMII port, the MAC is fixed at 1 Gbps and + * we need to configure the PCS only (if even that). */ - mac[port].speed = speed; + if (sja1105_supports_sgmii(priv, port)) + mac[port].speed = SJA1105_SPEED_1000MBPS; + else + mac[port].speed = speed; /* Write to the dynamic reconfiguration tables */ rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, @@ -757,16 +869,19 @@ static bool sja1105_phy_mode_mismatch(struct sja1105_private *priv, int port, case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: return (phy_mode != XMII_MODE_RGMII); + case PHY_INTERFACE_MODE_SGMII: + return (phy_mode != XMII_MODE_SGMII); default: return true; } } static void sja1105_mac_config(struct dsa_switch *ds, int port, - unsigned int link_an_mode, + unsigned int mode, const struct phylink_link_state *state) { struct sja1105_private *priv = ds->priv; + bool is_sgmii = sja1105_supports_sgmii(priv, port); if (sja1105_phy_mode_mismatch(priv, port, state->interface)) { dev_err(ds->dev, "Changing PHY mode to %s not supported!\n", @@ -774,10 +889,14 @@ static void sja1105_mac_config(struct dsa_switch *ds, int port, return; } - if (link_an_mode == MLO_AN_INBAND) { + if (phylink_autoneg_inband(mode) && !is_sgmii) { dev_err(ds->dev, "In-band AN not supported!\n"); return; } + + if (is_sgmii) + sja1105_sgmii_pcs_config(priv, phylink_autoneg_inband(mode), + false); } static void sja1105_mac_link_down(struct dsa_switch *ds, int port, @@ -798,6 +917,9 @@ static void sja1105_mac_link_up(struct dsa_switch *ds, int port, sja1105_adjust_port_config(priv, port, speed); + if (sja1105_supports_sgmii(priv, port) && !phylink_autoneg_inband(mode)) + sja1105_sgmii_pcs_force_speed(priv, speed); + sja1105_inhibit_tx(priv, BIT(port), false); } @@ -833,7 +955,8 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port, phylink_set(mask, 10baseT_Full); phylink_set(mask, 100baseT_Full); phylink_set(mask, 100baseT1_Full); - if (mii->xmii_mode[port] == XMII_MODE_RGMII) + if (mii->xmii_mode[port] == XMII_MODE_RGMII || + mii->xmii_mode[port] == XMII_MODE_SGMII) phylink_set(mask, 1000baseT_Full); bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); @@ -841,6 +964,38 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port, __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, 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 sja1105_find_static_fdb_entry(struct sja1105_private *priv, int port, const struct sja1105_l2_lookup_entry *requested) @@ -1367,6 +1522,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv, struct dsa_switch *ds = priv->ds; s64 t1, t2, t3, t4; s64 t12, t34; + u16 bmcr = 0; int rc, i; s64 now; @@ -1384,6 +1540,9 @@ int sja1105_static_config_reload(struct sja1105_private *priv, mac[i].speed = SJA1105_SPEED_AUTO; } + if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) + bmcr = sja1105_sgmii_read(priv, MII_BMCR); + /* No PTP operations can run right now */ mutex_lock(&priv->ptp_data.lock); @@ -1433,6 +1592,25 @@ int sja1105_static_config_reload(struct sja1105_private *priv, if (rc < 0) goto out; } + + if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) { + bool an_enabled = !!(bmcr & BMCR_ANENABLE); + + sja1105_sgmii_pcs_config(priv, an_enabled, false); + + if (!an_enabled) { + int speed = SPEED_UNKNOWN; + + if (bmcr & BMCR_SPEED1000) + speed = SPEED_1000; + else if (bmcr & BMCR_SPEED100) + speed = SPEED_100; + else if (bmcr & BMCR_SPEED10) + speed = SPEED_10; + + sja1105_sgmii_pcs_force_speed(priv, speed); + } + } out: mutex_unlock(&priv->mgmt_lock); @@ -1998,6 +2176,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .teardown = sja1105_teardown, .set_ageing_time = sja1105_set_ageing_time, .phylink_validate = sja1105_phylink_validate, + .phylink_mac_link_state = sja1105_mac_pcs_get_state, .phylink_mac_config = sja1105_mac_config, .phylink_mac_link_up = sja1105_mac_link_up, .phylink_mac_link_down = sja1105_mac_link_down, diff --git a/drivers/net/dsa/sja1105/sja1105_sgmii.h b/drivers/net/dsa/sja1105/sja1105_sgmii.h new file mode 100644 index 000000000000..24d9bc046e70 --- /dev/null +++ b/drivers/net/dsa/sja1105/sja1105_sgmii.h @@ -0,0 +1,53 @@ +/* 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 diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index 29b127f3bf9c..45da162ba268 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -474,6 +474,7 @@ static struct sja1105_regs sja1105pqrs_regs = { /* UM10944.pdf, Table 86, ACU Register overview */ .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808}, .pad_mii_id = {0x100810, 0x100811, 0x100812, 0x100813, 0x100814}, + .sgmii = 0x1F0000, .rmii_pll1 = 0x10000A, .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F}, .mac = {0x200, 0x202, 0x204, 0x206, 0x208},