Hello everyone,
This series introduces support for Ethernet in-band extensions, a
mechanism proposed by Cisco as part of the USXGMII spec.
The idea is to leverage the 7 bytes preamble to convey meaningful data,
in what's called an "extension".
This series adds the QUSGMII mode, which is a quad variant of the
USXGMII standard, and adds its support in the lan966x driver. In
QUSGMII, extensions can be used.
The only extension support thus far is the PCH mode, a way to convey
part of a timestamp into the ethernet preamble. That's a pretty
straightfoward extension, documented in the Cisco spec. Other extensions
can exist, each being identified by a 2 bits code in the preamble,
parsed by the hardware.
We therefore need an API to synchronise which mode is supported by a
given PHY, then a way to enable it in the PHY, from the MAC's control.
This is done through a new phy_driver callback, .inband_ext_config(),
that the MAC driver will call to ask a PHY driver to enable a given
extension.
The PCH mode that is added in this series is used to offload a bit
the MDIO bus when doing PHY-side timestamping, by conveying the nanoseconds
part of the timestamp into the preamble. The MAC driver then extracts
the timestamp (using lan966x's IFH mechanism), puts the nanosecond part
in the SKB. The RX deferred timestamping then asks the PHY for the rest
of the timestamp.
Other modes exists, such as Microchip's MCH mode, but this series only
include PCH since it's simple enough and keeps the code reviewable.
Thanks,
Maxime
Maxime Chevallier (6):
net: phy: Introduce QUSGMII PHY mode
dt-bindings: net: ethernet-controller: add QUSGMII mode
net: lan966x: Add QUSGMII support for lan966x
net: phy: Add support for inband extensions
net: lan966x: Allow using PCH extension for PTP
net: phy: micrel: Add QUSGMII support and PCH extension
.../bindings/net/ethernet-controller.yaml | 1 +
Documentation/networking/phy.rst | 9 ++
.../ethernet/microchip/lan966x/lan966x_main.c | 14 +--
.../ethernet/microchip/lan966x/lan966x_main.h | 6 ++
.../microchip/lan966x/lan966x_phylink.c | 9 +-
.../ethernet/microchip/lan966x/lan966x_port.c | 33 ++++--
.../ethernet/microchip/lan966x/lan966x_ptp.c | 93 +++++++++++++++-
.../ethernet/microchip/lan966x/lan966x_regs.h | 72 +++++++++++++
drivers/net/phy/micrel.c | 102 ++++++++++++++++--
drivers/net/phy/phy.c | 68 ++++++++++++
drivers/net/phy/phylink.c | 3 +
include/linux/phy.h | 28 ++++-
12 files changed, 413 insertions(+), 25 deletions(-)
--
2.36.1
The Lan996x controller supports the QUSGMII mode, which is very similar
to QSGMII in the way it's configured and the autonegociation
capababilities it provides.
This commit adds support for that mode, treating it most of the time
like QSGMII, making sure that we do configure the PCS how we should.
Signed-off-by: Maxime Chevallier <[email protected]>
---
.../ethernet/microchip/lan966x/lan966x_main.c | 2 ++
.../ethernet/microchip/lan966x/lan966x_main.h | 6 +++++
.../microchip/lan966x/lan966x_phylink.c | 9 +++++++-
.../ethernet/microchip/lan966x/lan966x_port.c | 22 ++++++++++++++-----
.../ethernet/microchip/lan966x/lan966x_regs.h | 6 +++++
5 files changed, 38 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
index ca1cef79b83f..b8c2ef905e46 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
@@ -728,6 +728,8 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_QSGMII,
port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_QUSGMII,
+ port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
index e6642083ab9e..304c784f48f6 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
@@ -452,4 +452,10 @@ static inline void lan_rmw(u32 val, u32 mask, struct lan966x *lan966x,
gcnt, gwidth, raddr, rinst, rcnt, rwidth));
}
+static inline bool lan966x_is_qsgmii(phy_interface_t mode)
+{
+ return (mode == PHY_INTERFACE_MODE_QSGMII) ||
+ (mode == PHY_INTERFACE_MODE_QUSGMII);
+}
+
#endif /* __LAN966X_MAIN_H__ */
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
index 38a7e95d69b4..96708352f53e 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
@@ -28,11 +28,18 @@ static int lan966x_phylink_mac_prepare(struct phylink_config *config,
phy_interface_t iface)
{
struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));
+ phy_interface_t serdes_mode = iface;
int err;
if (port->serdes) {
+ /* As far as the SerDes is concerned, QUSGMII is the same as
+ * QSGMII.
+ */
+ if (lan966x_is_qsgmii(iface))
+ serdes_mode = PHY_INTERFACE_MODE_QSGMII;
+
err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET,
- iface);
+ serdes_mode);
if (err) {
netdev_err(to_net_dev(config->dev),
"Could not set mode of SerDes\n");
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c
index f141644e4372..ef65a44b2d34 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c
@@ -168,7 +168,7 @@ static void lan966x_port_link_up(struct lan966x_port *port)
/* Also the GIGA_MODE_ENA(1) needs to be set regardless of the
* port speed for QSGMII ports.
*/
- if (config->portmode == PHY_INTERFACE_MODE_QSGMII)
+ if (lan966x_is_qsgmii(config->portmode))
mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA_SET(1);
lan_wr(config->duplex | mode,
@@ -331,10 +331,14 @@ int lan966x_port_pcs_set(struct lan966x_port *port,
struct lan966x *lan966x = port->lan966x;
bool inband_aneg = false;
bool outband;
+ bool full_preamble = false;
+
+ if (config->portmode == PHY_INTERFACE_MODE_QUSGMII)
+ full_preamble = true;
if (config->inband) {
if (config->portmode == PHY_INTERFACE_MODE_SGMII ||
- config->portmode == PHY_INTERFACE_MODE_QSGMII)
+ lan966x_is_qsgmii(config->portmode))
inband_aneg = true; /* Cisco-SGMII in-band-aneg */
else if (config->portmode == PHY_INTERFACE_MODE_1000BASEX &&
config->autoneg)
@@ -345,9 +349,15 @@ int lan966x_port_pcs_set(struct lan966x_port *port,
outband = true;
}
- /* Disable or enable inband */
- lan_rmw(DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA_SET(outband),
- DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA,
+ /* Disable or enable inband.
+ * For QUSGMII, we rely on the preamble to transmit data such as
+ * timestamps, therefore force full preamble transmission, and prevent
+ * premable shortening
+ */
+ lan_rmw(DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA_SET(outband) |
+ DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA_SET(full_preamble),
+ DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA |
+ DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA,
lan966x, DEV_PCS1G_MODE_CFG(port->chip_port));
/* Enable PCS */
@@ -396,7 +406,7 @@ void lan966x_port_init(struct lan966x_port *port)
if (lan966x->fdma)
lan966x_fdma_netdev_init(lan966x, port->dev);
- if (config->portmode != PHY_INTERFACE_MODE_QSGMII)
+ if (!lan966x_is_qsgmii(config->portmode))
return;
lan_rmw(DEV_CLOCK_CFG_PCS_RX_RST_SET(0) |
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h
index 2f59285bef29..d4839e4b8ed5 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h
@@ -504,6 +504,12 @@ enum lan966x_target {
#define DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA_GET(x)\
FIELD_GET(DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA, x)
+#define DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA BIT(1)
+#define DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA_SET(x)\
+ FIELD_PREP(DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA, x)
+#define DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA_GET(x)\
+ FIELD_GET(DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA, x)
+
/* DEV:PCS1G_CFG_STATUS:PCS1G_SD_CFG */
#define DEV_PCS1G_SD_CFG(t) __REG(TARGET_DEV, t, 8, 72, 0, 1, 68, 8, 0, 1, 4)
--
2.36.1
Add a new QUSGMII mode, standing for "Quad Universal Serial Gigabit
Media Independent Interface", a derivative of USGMII which, similarly to
QSGMII, allows to multiplex 4 1Gbps links to a Quad-PHY.
The main difference with QSGMII is that QUSGMII can include an extension
instead of the standard 7bytes ethernet preamble, allowing to convey
arbitrary data such as Timestamps.
Signed-off-by: Maxime Chevallier <[email protected]>
---
Documentation/devicetree/bindings/net/ethernet-controller.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
index 4f15463611f8..dccde2033697 100644
--- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
@@ -67,6 +67,7 @@ properties:
- gmii
- sgmii
- qsgmii
+ - qusgmii
- tbi
- rev-mii
- rmii
--
2.36.1
Hi,
On Thu, May 19, 2022 at 03:56:44PM +0200, Maxime Chevallier wrote:
> diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
> index e6642083ab9e..304c784f48f6 100644
> --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
> +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
> @@ -452,4 +452,10 @@ static inline void lan_rmw(u32 val, u32 mask, struct lan966x *lan966x,
> gcnt, gwidth, raddr, rinst, rcnt, rwidth));
> }
>
> +static inline bool lan966x_is_qsgmii(phy_interface_t mode)
> +{
> + return (mode == PHY_INTERFACE_MODE_QSGMII) ||
> + (mode == PHY_INTERFACE_MODE_QUSGMII);
> +}
Maybe linux/phy.h should provide a helper, something like:
phy_interface_serdes_lanes()
that returns how many serdes lanes the interface mode uses?
> diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
> index 38a7e95d69b4..96708352f53e 100644
> --- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
> +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
> @@ -28,11 +28,18 @@ static int lan966x_phylink_mac_prepare(struct phylink_config *config,
> phy_interface_t iface)
> {
> struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));
> + phy_interface_t serdes_mode = iface;
> int err;
>
> if (port->serdes) {
> + /* As far as the SerDes is concerned, QUSGMII is the same as
> + * QSGMII.
> + */
> + if (lan966x_is_qsgmii(iface))
> + serdes_mode = PHY_INTERFACE_MODE_QSGMII;
> +
> err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET,
> - iface);
> + serdes_mode);
I don't think that the ethernet MAC driver should be changing the
interface mode before passing it down to the generic PHY layer -
phy_set_mode_ext() is defined to take the phy interface mode, and any
aliasing of modes should really be up to the generic PHY driver not
the ethernet MAC driver.
Thanks.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
This commit adds the logic to use the PCH mode for timestamp passing in
ingress traffic for PTP. The tricky part is that we need to configure
both the PHY and the MAC, since the MAC will extract the timetsamp
reported by the PHY from the preamble and set it into the SKB.
Note that with the PCH mode, we only get the RX timestamp that way, the
TX timestamps are still reported OOB by the PHY.
Note that only the nanoseconds part is extracted from the PCH extension,
since there's not enough room in the 4 bytes extension to pass a full
80bits timestamp. The seconds part of the timestamp still needs to get
retrieved from the PHY using an out-of-band mechanism.
Signed-off-by: Maxime Chevallier <[email protected]>
---
.../ethernet/microchip/lan966x/lan966x_main.c | 12 +--
.../ethernet/microchip/lan966x/lan966x_port.c | 11 +++
.../ethernet/microchip/lan966x/lan966x_ptp.c | 93 ++++++++++++++++++-
.../ethernet/microchip/lan966x/lan966x_regs.h | 66 +++++++++++++
4 files changed, 171 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
index b8c2ef905e46..d5d65cc9fc76 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
@@ -400,13 +400,11 @@ static int lan966x_port_ioctl(struct net_device *dev, struct ifreq *ifr,
{
struct lan966x_port *port = netdev_priv(dev);
- if (!phy_has_hwtstamp(dev->phydev) && port->lan966x->ptp) {
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return lan966x_ptp_hwtstamp_set(port, ifr);
- case SIOCGHWTSTAMP:
- return lan966x_ptp_hwtstamp_get(port, ifr);
- }
+ switch (cmd) {
+ case SIOCSHWTSTAMP:
+ return lan966x_ptp_hwtstamp_set(port, ifr);
+ case SIOCGHWTSTAMP:
+ return lan966x_ptp_hwtstamp_get(port, ifr);
}
if (!dev->phydev)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c
index ef65a44b2d34..3a0aaf566698 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c
@@ -360,6 +360,17 @@ int lan966x_port_pcs_set(struct lan966x_port *port,
DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA,
lan966x, DEV_PCS1G_MODE_CFG(port->chip_port));
+ if (full_preamble)
+ lan_rmw(DEV_ENABLE_CONFIG_MM_TX_ENA_SET(1) |
+ DEV_ENABLE_CONFIG_MM_RX_ENA_SET(1),
+ DEV_ENABLE_CONFIG_MM_TX_ENA |
+ DEV_ENABLE_CONFIG_MM_RX_ENA,
+ lan966x, DEV_ENABLE_CONFIG(port->chip_port));
+
+ lan_rmw(SYS_PTP_MODE_CFG_PTP_MODE_VAL_SET(1),
+ SYS_PTP_MODE_CFG_PTP_MODE_VAL,
+ lan966x, SYS_PTP_MODE_CFG(port->chip_port, 0));
+
/* Enable PCS */
lan_wr(DEV_PCS1G_CFG_PCS_ENA_SET(1),
lan966x, DEV_PCS1G_CFG(port->chip_port));
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
index ae782778d6dd..f9dc0861f038 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
@@ -35,11 +35,28 @@ static u64 lan966x_ptp_get_nominal_value(void)
return res;
}
-int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
+/* Enable or disable PCH timestamp transmission. This uses the USGMII PCH
+ * extensions to transmit the timestamps in the frame preamble.
+ */
+static void lan966x_ptp_pch_configure(struct lan966x_port *port, bool enable)
+{
+ lan_rmw(SYS_PCH_CFG_PCH_SUB_PORT_ID_SET(port->chip_port % 4) |
+ SYS_PCH_CFG_PCH_TX_MODE_SET(enable) |
+ SYS_PCH_CFG_PCH_RX_MODE_SET(enable),
+ SYS_PCH_CFG_PCH_SUB_PORT_ID |
+ SYS_PCH_CFG_PCH_TX_MODE |
+ SYS_PCH_CFG_PCH_RX_MODE,
+ port->lan966x, SYS_PCH_CFG(port->chip_port));
+}
+
+static int lan966x_ptp_mac_hwtstamp_set(struct lan966x_port *port,
+ struct ifreq *ifr,
+ bool inband)
{
struct lan966x *lan966x = port->lan966x;
struct hwtstamp_config cfg;
struct lan966x_phc *phc;
+ bool timestamp_in_pch = false;
/* For now don't allow to run ptp on ports that are part of a bridge,
* because in case of transparent clock the HW will still forward the
@@ -88,16 +105,48 @@ int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
return -ERANGE;
}
+ if (inband &&
+ port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP &&
+ port->config.portmode == PHY_INTERFACE_MODE_QUSGMII)
+ timestamp_in_pch = true;
+
/* Commit back the result & save it */
mutex_lock(&lan966x->ptp_lock);
phc = &lan966x->phc[LAN966X_PHC_PORT];
memcpy(&phc->hwtstamp_config, &cfg, sizeof(cfg));
+ lan966x_ptp_pch_configure(port, timestamp_in_pch);
mutex_unlock(&lan966x->ptp_lock);
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+
}
-int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr)
+int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr)
+{
+ struct phy_device *phydev = port->dev->phydev;
+ int ret;
+
+ if (!phy_has_hwtstamp(phydev) && port->lan966x->ptp)
+ return lan966x_ptp_mac_hwtstamp_set(port, ifr, false);
+
+ ret = phy_mii_ioctl(phydev, ifr, SIOCSHWTSTAMP);
+ if (ret)
+ return ret;
+
+ if (phy_inband_ext_available(phydev, PHY_INBAND_EXT_PCH)) {
+ ret = phy_inband_ext_enable(phydev, PHY_INBAND_EXT_PCH);
+ if (ret)
+ return ret;
+
+ ret = lan966x_ptp_mac_hwtstamp_set(port, ifr, true);
+ if (ret)
+ return phy_inband_ext_disable(phydev, PHY_INBAND_EXT_PCH);
+ }
+
+ return ret;
+}
+
+static int lan966x_ptp_mac_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr)
{
struct lan966x *lan966x = port->lan966x;
struct lan966x_phc *phc;
@@ -107,6 +156,14 @@ int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr)
sizeof(phc->hwtstamp_config)) ? -EFAULT : 0;
}
+int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr)
+{
+ if (!phy_has_hwtstamp(port->dev->phydev) && port->lan966x->ptp)
+ return lan966x_ptp_mac_hwtstamp_get(port, ifr);
+
+ return phy_mii_ioctl(port->dev->phydev, ifr, SIOCGHWTSTAMP);
+}
+
static int lan966x_ptp_classify(struct lan966x_port *port, struct sk_buff *skb)
{
struct ptp_header *header;
@@ -182,8 +239,16 @@ int lan966x_ptp_txtstamp_request(struct lan966x_port *port,
LAN966X_SKB_CB(skb)->jiffies = jiffies;
lan966x->ptp_skbs++;
- port->ts_id++;
- if (port->ts_id == LAN966X_MAX_PTP_ID)
+
+ /* If PHC is enabled, the subns part of the ID is not passed in the PCH
+ * header. So shift it by 2 to skip the subns part
+ */
+ if (phy_inband_ext_enabled(port->dev->phydev, PHY_INBAND_EXT_PCH))
+ port->ts_id = (((port->ts_id >> 2) + 1) << 2);
+ else
+ port->ts_id++;
+
+ if (port->ts_id >= LAN966X_MAX_PTP_ID)
port->ts_id = 0;
spin_unlock_irqrestore(&lan966x->ptp_ts_id_lock, flags);
@@ -285,6 +350,26 @@ irqreturn_t lan966x_ptp_irq_handler(int irq, void *args)
/* Read RX timestamping to get the ID */
id = lan_rd(lan966x, PTP_TWOSTEP_STAMP);
+ /* If PCH is enabled, there is a "feature" that also the MAC
+ * will generate an interrupt for transmitted frames. This
+ * interrupt should be ignored, so clear the allocated resources
+ * and try to get the next timestamp. Maybe should clean the
+ * resources on the TX side?
+ */
+ if (phy_inband_ext_enabled(port->dev->phydev, PHY_INBAND_EXT_PCH)) {
+ spin_lock(&lan966x->ptp_ts_id_lock);
+ lan966x->ptp_skbs--;
+ spin_unlock(&lan966x->ptp_ts_id_lock);
+
+ dev_kfree_skb_any(skb_match);
+
+ lan_rmw(PTP_TWOSTEP_CTRL_NXT_SET(1),
+ PTP_TWOSTEP_CTRL_NXT,
+ lan966x, PTP_TWOSTEP_CTRL);
+
+ continue;
+ }
+
spin_lock_irqsave(&port->tx_skbs.lock, flags);
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
if (LAN966X_SKB_CB(skb)->ts_id != id)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h
index d4839e4b8ed5..e0d4364ee204 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h
@@ -585,6 +585,27 @@ enum lan966x_target {
#define DEV_PCS1G_STICKY_LINK_DOWN_STICKY_GET(x)\
FIELD_GET(DEV_PCS1G_STICKY_LINK_DOWN_STICKY, x)
+/* DEV:MM_CONFIG:ENABLE_CONFIG */
+#define DEV_ENABLE_CONFIG(t) __REG(TARGET_DEV, t, 8, 156, 0, 1, 8, 0, 0, 1, 4)
+
+#define DEV_ENABLE_CONFIG_KEEP_S_AFTER_D BIT(8)
+#define DEV_ENABLE_CONFIG_KEEP_S_AFTER_D_SET(x)\
+ FIELD_PREP(DEV_ENABLE_CONFIG_KEEP_S_AFTER_D, x)
+#define DEV_ENABLE_CONFIG_KEEP_S_AFTER_D_GET(x)\
+ FIELD_GET(DEV_ENABLE_CONFIG_KEEP_S_AFTER_D, x)
+
+#define DEV_ENABLE_CONFIG_MM_TX_ENA BIT(4)
+#define DEV_ENABLE_CONFIG_MM_TX_ENA_SET(x)\
+ FIELD_PREP(DEV_ENABLE_CONFIG_MM_TX_ENA, x)
+#define DEV_ENABLE_CONFIG_MM_TX_ENA_GET(x)\
+ FIELD_GET(DEV_ENABLE_CONFIG_MM_TX_ENA, x)
+
+#define DEV_ENABLE_CONFIG_MM_RX_ENA BIT(0)
+#define DEV_ENABLE_CONFIG_MM_RX_ENA_SET(x)\
+ FIELD_PREP(DEV_ENABLE_CONFIG_MM_RX_ENA, x)
+#define DEV_ENABLE_CONFIG_MM_RX_ENA_GET(x)\
+ FIELD_GET(DEV_ENABLE_CONFIG_MM_RX_ENA, x)
+
/* FDMA:FDMA:FDMA_CH_ACTIVATE */
#define FDMA_CH_ACTIVATE __REG(TARGET_FDMA, 0, 1, 8, 0, 1, 428, 0, 0, 1, 4)
@@ -792,6 +813,15 @@ enum lan966x_target {
#define PTP_TWOSTEP_STAMP_STAMP_NSEC_GET(x)\
FIELD_GET(PTP_TWOSTEP_STAMP_STAMP_NSEC, x)
+/* SYS:PTPPORT:PTP_MODE_CFG */
+#define SYS_PTP_MODE_CFG(g, r) __REG(TARGET_SYS, 0, 1, 4452, g, 10, 28, 20, r, 2, 4)
+
+#define SYS_PTP_MODE_CFG_PTP_MODE_VAL GENMASK(1, 0)
+#define SYS_PTP_MODE_CFG_PTP_MODE_VAL_SET(x)\
+ FIELD_PREP(SYS_PTP_MODE_CFG_PTP_MODE_VAL, x)
+#define SYS_PTP_MODE_CFG_PTP_MODE_VAL_GET(x)\
+ FIELD_GET(SYS_PTP_MODE_CFG_PTP_MODE_VAL, x)
+
/* DEVCPU_QS:XTR:XTR_GRP_CFG */
#define QS_XTR_GRP_CFG(r) __REG(TARGET_QS, 0, 1, 0, 0, 1, 36, 0, r, 2, 4)
@@ -972,6 +1002,21 @@ enum lan966x_target {
#define REW_PORT_CFG_NO_REWRITE_GET(x)\
FIELD_GET(REW_PORT_CFG_NO_REWRITE, x)
+/* REW:PORT:PTP_MISC_CFG */
+#define REW_PTP_MISC_CFG(g) __REG(TARGET_REW, 0, 1, 0, g, 10, 128, 80, 0, 1, 4)
+
+#define REW_PTP_MISC_CFG_PTP_RSRV_MOVEBACK BIT(2)
+#define REW_PTP_MISC_CFG_PTP_RSRV_MOVEBACK_SET(x)\
+ FIELD_PREP(REW_PTP_MISC_CFG_PTP_RSRV_MOVEBACK, x)
+#define REW_PTP_MISC_CFG_PTP_RSRV_MOVEBACK_GET(x)\
+ FIELD_GET(REW_PTP_MISC_CFG_PTP_RSRV_MOVEBACK, x)
+
+#define REW_PTP_MISC_CFG_PTP_SIGNATURE_SEL GENMASK(1, 0)
+#define REW_PTP_MISC_CFG_PTP_SIGNATURE_SEL_SET(x)\
+ FIELD_PREP(REW_PTP_MISC_CFG_PTP_SIGNATURE_SEL, x)
+#define REW_PTP_MISC_CFG_PTP_SIGNATURE_SEL_GET(x)\
+ FIELD_GET(REW_PTP_MISC_CFG_PTP_SIGNATURE_SEL, x)
+
/* SYS:SYSTEM:RESET_CFG */
#define SYS_RESET_CFG __REG(TARGET_SYS, 0, 1, 4128, 0, 1, 168, 0, 0, 1, 4)
@@ -1101,4 +1146,25 @@ enum lan966x_target {
#define SYS_RAM_INIT_RAM_INIT_GET(x)\
FIELD_GET(SYS_RAM_INIT_RAM_INIT, x)
+/* SYS:PTPPORT:PCH_CFG */
+#define SYS_PCH_CFG(g) __REG(TARGET_SYS, 0, 1, 4452, g, 10, 28, 0, 0, 1, 4)
+
+#define SYS_PCH_CFG_PCH_SUB_PORT_ID GENMASK(10, 7)
+#define SYS_PCH_CFG_PCH_SUB_PORT_ID_SET(x)\
+ FIELD_PREP(SYS_PCH_CFG_PCH_SUB_PORT_ID, x)
+#define SYS_PCH_CFG_PCH_SUB_PORT_ID_GET(x)\
+ FIELD_GET(SYS_PCH_CFG_PCH_SUB_PORT_ID, x)
+
+#define SYS_PCH_CFG_PCH_TX_MODE GENMASK(6, 5)
+#define SYS_PCH_CFG_PCH_TX_MODE_SET(x)\
+ FIELD_PREP(SYS_PCH_CFG_PCH_TX_MODE, x)
+#define SYS_PCH_CFG_PCH_TX_MODE_GET(x)\
+ FIELD_GET(SYS_MAC_FC_CFG_PAUSE_VAL_CFG, x)
+
+#define SYS_PCH_CFG_PCH_RX_MODE GENMASK(4, 2)
+#define SYS_PCH_CFG_PCH_RX_MODE_SET(x)\
+ FIELD_PREP(SYS_PCH_CFG_PCH_RX_MODE, x)
+#define SYS_PCH_CFG_PCH_RX_MODE_GET(x)\
+ FIELD_GET(SYS_PCH_CFG_PCH_RX_MODE, x)
+
#endif /* _LAN966X_REGS_H_ */
--
2.36.1
This commit adds support for the PCH extension in the Lan8814 PHY,
allowing the PHY to report RX timestamps to the MAC in the ethernet
preamble.
When using the PCH extension, the PHY will only report the nanoseconds
part of the timestamp in-band, and the seconds part out-of-band.
The main goal in the end is to lower the pressure on the MDIO bus, which
may get pushed to its limit on 48 ports switches doing PTP at a high
rate.
Signed-off-by: Maxime Chevallier <[email protected]>
---
drivers/net/phy/micrel.c | 102 ++++++++++++++++++++++++++++++++++++---
1 file changed, 96 insertions(+), 6 deletions(-)
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index f537e61cb61d..904b46701782 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -116,6 +116,10 @@
#define LTC_HARD_RESET 0x023F
#define LTC_HARD_RESET_ BIT(0)
+#define TSU_GENERAL_CONFIG 0x2C0
+#define TSU_GENERAL_CONFIG_TSU_ENABLE_ BIT(0)
+#define TSU_GENERAL_CONFIG_TSU_ENABLE_PCH_ BIT(1)
+
#define TSU_HARD_RESET 0x02C1
#define TSU_HARD_RESET_ BIT(0)
@@ -139,6 +143,7 @@
#define PTP_OPERATING_MODE 0x0241
#define PTP_OPERATING_MODE_STANDALONE_ BIT(0)
+#define PTP_OPERATING_MODE_PCH_ BIT(1)
#define PTP_TX_MOD 0x028F
#define PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_ BIT(12)
@@ -227,6 +232,15 @@
#define PS_TO_REG 200
#define FIFO_SIZE 8
+struct lan8814_skb_cb {
+ u8 rew_op;
+ u16 ts_id;
+ unsigned long jiffies;
+};
+
+#define LAN8814_SKB_CB(skb) \
+ ((struct lan8814_skb_cb *)((skb)->cb))
+
struct kszphy_hw_stat {
const char *string;
u8 reg;
@@ -1809,6 +1823,16 @@ static void lan8814_ptp_rx_ts_get(struct phy_device *phydev,
*seq_id = lanphy_read_page_reg(phydev, 5, PTP_RX_MSG_HEADER2);
}
+static void lan8814_ptp_rx_ts_get_partial(struct phy_device *phydev,
+ u32 *seconds, u16 *seq_id)
+{
+ *seconds = lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_HI);
+ *seconds = (*seconds << 16) |
+ lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_LO);
+
+ *seq_id = lanphy_read_page_reg(phydev, 5, PTP_RX_MSG_HEADER2);
+}
+
static void lan8814_ptp_tx_ts_get(struct phy_device *phydev,
u32 *seconds, u32 *nano_seconds, u16 *seq_id)
{
@@ -1955,6 +1979,13 @@ static int lan8814_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
lan8814_flush_fifo(ptp_priv->phydev, false);
lan8814_flush_fifo(ptp_priv->phydev, true);
+ if (phydev->interface == PHY_INTERFACE_MODE_QUSGMII &&
+ ptp_priv->hwts_tx_type == HWTSTAMP_TX_ON)
+ phy_inband_ext_set_available(phydev, PHY_INBAND_EXT_PCH,
+ PHY_INBAND_EXT_PCH);
+ else
+ phy_inband_ext_set_available(phydev, PHY_INBAND_EXT_PCH, 0);
+
return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0;
}
@@ -2283,11 +2314,17 @@ static int lan8814_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm)
return 0;
}
-static void lan8814_get_sig_tx(struct sk_buff *skb, u16 *sig)
+static void lan8814_get_sig_tx(struct phy_device *phydev,
+ struct sk_buff *skb, u16 *sig)
{
struct ptp_header *ptp_header;
u32 type;
+ if (phy_inband_ext_enabled(phydev, PHY_INBAND_EXT_PCH)) {
+ *sig = LAN8814_SKB_CB(skb)->ts_id >> 2;
+ return;
+ }
+
type = ptp_classify_raw(skb);
ptp_header = ptp_parse_header(skb, type);
@@ -2309,7 +2346,7 @@ static void lan8814_dequeue_tx_skb(struct kszphy_ptp_priv *ptp_priv)
spin_lock_irqsave(&ptp_priv->tx_queue.lock, flags);
skb_queue_walk_safe(&ptp_priv->tx_queue, skb, skb_tmp) {
- lan8814_get_sig_tx(skb, &skb_sig);
+ lan8814_get_sig_tx(phydev, skb, &skb_sig);
if (memcmp(&skb_sig, &seq_id, sizeof(seq_id)))
continue;
@@ -2367,8 +2404,20 @@ static bool lan8814_match_skb(struct kszphy_ptp_priv *ptp_priv,
if (ret) {
shhwtstamps = skb_hwtstamps(skb);
- memset(shhwtstamps, 0, sizeof(*shhwtstamps));
- shhwtstamps->hwtstamp = ktime_set(rx_ts->seconds, rx_ts->nsec);
+
+ if (phy_inband_ext_enabled(ptp_priv->phydev, PHY_INBAND_EXT_PCH)) {
+ /* When using the PCH extension, we get the seconds part
+ * from MDIO accesses, but the seconds part gets
+ * set by the MAC driver according to the PCH data in the
+ * preamble
+ */
+ struct timespec64 ts = ktime_to_timespec64(shhwtstamps->hwtstamp);
+
+ shhwtstamps->hwtstamp = ktime_set(rx_ts->seconds, ts.tv_nsec);
+ } else {
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+ shhwtstamps->hwtstamp = ktime_set(rx_ts->seconds, rx_ts->nsec);
+ }
netif_rx(skb);
}
@@ -2387,8 +2436,17 @@ static void lan8814_get_rx_ts(struct kszphy_ptp_priv *ptp_priv)
if (!rx_ts)
return;
- lan8814_ptp_rx_ts_get(phydev, &rx_ts->seconds, &rx_ts->nsec,
- &rx_ts->seq_id);
+ /* When using PCH mode, the nanoseconds part of the timestamp is
+ * transmitted inband through the ethernet preamble. We only need
+ * to retrieve the seconds part along with the seq_id, the MAC
+ * driver will fill-in the nanoseconds part itself
+ */
+ if (phy_inband_ext_enabled(ptp_priv->phydev, PHY_INBAND_EXT_PCH))
+ lan8814_ptp_rx_ts_get_partial(phydev, &rx_ts->seconds,
+ &rx_ts->seq_id);
+ else
+ lan8814_ptp_rx_ts_get(phydev, &rx_ts->seconds,
+ &rx_ts->nsec, &rx_ts->seq_id);
/* If we failed to match the skb add it to the queue for when
* the frame will come
@@ -2682,6 +2740,36 @@ static int lan8814_set_tunable(struct phy_device *phydev,
}
}
+static int lan8814_inband_ext_config(struct phy_device *phydev, u32 mask, u32 ext)
+{
+ u32 tsu_cfg;
+
+ if (!(mask & PHY_INBAND_EXT_PCH))
+ return -EOPNOTSUPP;
+
+ tsu_cfg = ~TSU_GENERAL_CONFIG_TSU_ENABLE_;
+
+ lanphy_write_page_reg(phydev, 5, TSU_GENERAL_CONFIG, tsu_cfg);
+
+ if (ext & PHY_INBAND_EXT_PCH) {
+ lanphy_write_page_reg(phydev, 4, PTP_OPERATING_MODE,
+ PTP_OPERATING_MODE_PCH_);
+
+ tsu_cfg |= TSU_GENERAL_CONFIG_TSU_ENABLE_PCH_;
+ } else {
+ lanphy_write_page_reg(phydev, 4, PTP_OPERATING_MODE,
+ PTP_OPERATING_MODE_STANDALONE_);
+
+ tsu_cfg &= ~TSU_GENERAL_CONFIG_TSU_ENABLE_PCH_;
+ }
+
+ tsu_cfg |= TSU_GENERAL_CONFIG_TSU_ENABLE_;
+
+ lanphy_write_page_reg(phydev, 5, TSU_GENERAL_CONFIG, tsu_cfg);
+
+ return 0;
+}
+
static int lan8814_config_init(struct phy_device *phydev)
{
int val;
@@ -2914,6 +3002,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id = PHY_ID_LAN8814,
.phy_id_mask = MICREL_PHY_ID_MASK,
.name = "Microchip INDY Gigabit Quad PHY",
+ .inband_ext = PHY_INBAND_EXT_PCH,
.config_init = lan8814_config_init,
.probe = lan8814_probe,
.soft_reset = genphy_soft_reset,
@@ -2927,6 +3016,7 @@ static struct phy_driver ksphy_driver[] = {
.resume = kszphy_resume,
.config_intr = lan8814_config_intr,
.handle_interrupt = lan8814_handle_interrupt,
+ .inband_ext_config = lan8814_inband_ext_config,
}, {
.phy_id = PHY_ID_LAN8804,
.phy_id_mask = MICREL_PHY_ID_MASK,
--
2.36.1
On Thu, 19 May 2022 15:56:43 +0200, Maxime Chevallier wrote:
> Add a new QUSGMII mode, standing for "Quad Universal Serial Gigabit
> Media Independent Interface", a derivative of USGMII which, similarly to
> QSGMII, allows to multiplex 4 1Gbps links to a Quad-PHY.
>
> The main difference with QSGMII is that QUSGMII can include an extension
> instead of the standard 7bytes ethernet preamble, allowing to convey
> arbitrary data such as Timestamps.
>
> Signed-off-by: Maxime Chevallier <[email protected]>
> ---
> Documentation/devicetree/bindings/net/ethernet-controller.yaml | 1 +
> 1 file changed, 1 insertion(+)
>
Acked-by: Rob Herring <[email protected]>
Hello Russell,
On Thu, 19 May 2022 15:26:23 +0100
"Russell King (Oracle)" <[email protected]> wrote:
> Hi,
>
> On Thu, May 19, 2022 at 03:56:44PM +0200, Maxime Chevallier wrote:
> > diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
> > b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index
> > e6642083ab9e..304c784f48f6 100644 ---
> > a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++
> > b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -452,4
> > +452,10 @@ static inline void lan_rmw(u32 val, u32 mask, struct
> > lan966x *lan966x, gcnt, gwidth, raddr, rinst, rcnt, rwidth)); }
> >
> > +static inline bool lan966x_is_qsgmii(phy_interface_t mode)
> > +{
> > + return (mode == PHY_INTERFACE_MODE_QSGMII) ||
> > + (mode == PHY_INTERFACE_MODE_QUSGMII);
> > +}
>
> Maybe linux/phy.h should provide a helper, something like:
>
> phy_interface_serdes_lanes()
>
> that returns how many serdes lanes the interface mode uses?
Sorry about the delayed answer, I was resuming the work on this, and
realised that although a helper would be indeed great, especially for
generic PHY drivers, it won't help much in this case since
QSGMII/QUSGMII both use 1 serdes lane, as SGMII and such. If I'm not
mistaken, QSGMII is SGMII clocked at 5Gbps with a specific preamble
allowing to identify the src/dst port.
We could however imagine a helper identifying the number of links, or
lanes (or another terminology) that is carried by a given mode. I know
that besides QSGMII for 4 ports, there exists PSGMII for 5 ports, and
OSGMII for 8 ports, so this would definitely prove useful in the
future.
Sorry if this ends-up being a misunderstanding on the terminology,
we're probably already talking about the same thing, but I think that
"serdes lane" would better describe the number of physical differential
pairs that creates the link (like, 1 for SGMII, 2 for RXAUI, 4 for XAUI
and so on).
maybe something like
phy_interface_lines() or
phy_interface_num_ports() or simply
phy_interface_lanes()
> > diff --git
> > a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
> > b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c index
> > 38a7e95d69b4..96708352f53e 100644 ---
> > a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c +++
> > b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c @@
> > -28,11 +28,18 @@ static int lan966x_phylink_mac_prepare(struct
> > phylink_config *config, phy_interface_t iface) { struct
> > lan966x_port *port = netdev_priv(to_net_dev(config->dev));
> > + phy_interface_t serdes_mode = iface;
> > int err;
> >
> > if (port->serdes) {
> > + /* As far as the SerDes is concerned, QUSGMII is
> > the same as
> > + * QSGMII.
> > + */
> > + if (lan966x_is_qsgmii(iface))
> > + serdes_mode = PHY_INTERFACE_MODE_QSGMII;
> > +
> > err = phy_set_mode_ext(port->serdes,
> > PHY_MODE_ETHERNET,
> > - iface);
> > + serdes_mode);
>
> I don't think that the ethernet MAC driver should be changing the
> interface mode before passing it down to the generic PHY layer -
> phy_set_mode_ext() is defined to take the phy interface mode, and any
> aliasing of modes should really be up to the generic PHY driver not
> the ethernet MAC driver.
Indeed, I'll split the series so that we first add support for the new
mode, and then send separate series for the generic PHY driver on one
side, and inband extensions on the other one.
Thanks,
Maxime
> Thanks.
>