Commit 9268abe6 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-lan969x-add-rgmii-support'

Daniel Machon says:

====================
net: lan969x: add RGMII support

== Description:

This series is the fourth of a multi-part series, that prepares and adds
support for the new lan969x switch driver.

The upstreaming efforts is split into multiple series (might change a
bit as we go along):

        1) Prepare the Sparx5 driver for lan969x (merged)

        2) Add support for lan969x (same basic features as Sparx5
           provides excl. FDMA and VCAP, merged).

        3) Add lan969x VCAP functionality (merged).

    --> 4) Add RGMII support.

        5) Add FDMA support.

== RGMII support:

The lan969x switch device includes two RGMII port interfaces (port 28
and 29) supporting data speeds of 1 Gbps, 100 Mbps and 10 Mbps.

== Patch breakdown:

Patch #1 does some preparation work.

Patch #2 adds new function: is_port_rgmii() to the match data ops.

Patch #3 uses the is_port_rgmii() in a number of places.

Patch #4 makes sure that we do not configure an RGMII device as a
         low-speed device, when doing a port config.

Patch #5 makes sure we only return the PCS if the port mode requires
         it.

Patch #6 adds checks for RGMII PHY modes in sparx5_verify_speeds().

Patch #7 adds registers required to configure RGMII.

Patch #8 adds RGMII implementation.

Patch #9 documents RGMII delays in the dt-bindings.

Details are in the commit description of the individual patches

v4: https://lore.kernel.org/20241213-sparx5-lan969x-switch-driver-4-v4-0-d1a72c9c4714@microchip.com
v3: https://lore.kernel.org/20241118-sparx5-lan969x-switch-driver-4-v3-0-3cefee5e7e3a@microchip.com
v2: https://lore.kernel.org/20241113-sparx5-lan969x-switch-driver-4-v2-0-0db98ac096d1@microchip.com
v1: https://lore.kernel.org/20241106-sparx5-lan969x-switch-driver-4-v1-0-f7f7316436bd@microchip.com
====================

Link: https://patch.msgid.link/20241220-sparx5-lan969x-switch-driver-4-v5-0-fa8ba5dff732@microchip.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 847cf3b9 f0706c04
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -129,6 +129,24 @@ properties:
            minimum: 0
            maximum: 383

          rx-internal-delay-ps:
            description:
              RGMII Receive Clock Delay defined in pico seconds, used to select
              the DLL phase shift between 1000 ps (45 degree shift at 1Gbps) and
              3300 ps (147 degree shift at 1Gbps). A value of 0 ps will disable
              any delay. The Default is no delay.
            enum: [0, 1000, 1700, 2000, 2500, 3000, 3300]
            default: 0

          tx-internal-delay-ps:
            description:
              RGMII Transmit Clock Delay defined in pico seconds, used to select
              the DLL phase shift between 1000 ps (45 degree shift at 1Gbps) and
              3300 ps (147 degree shift at 1Gbps). A value of 0 ps will disable
              any delay. The Default is no delay.
            enum: [0, 1000, 1700, 2000, 2500, 3000, 3300]
            default: 0

        required:
          - reg
          - phys
+2 −1
Original line number Diff line number Diff line
@@ -20,7 +20,8 @@ sparx5-switch-$(CONFIG_LAN969X_SWITCH) += lan969x/lan969x_regs.o \
					  lan969x/lan969x.o \
					  lan969x/lan969x_calendar.o \
					  lan969x/lan969x_vcap_ag_api.o \
					  lan969x/lan969x_vcap_impl.o
					  lan969x/lan969x_vcap_impl.o \
					  lan969x/lan969x_rgmii.o

# Provide include files
ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap
+5 −0
Original line number Diff line number Diff line
@@ -90,9 +90,12 @@ static const struct sparx5_main_io_resource lan969x_main_iomap[] = {
	{ TARGET_DEV2G5 + 27,         0x30d8000, 1 }, /* 0xe30d8000 */
	{ TARGET_DEV10G +  9,         0x30dc000, 1 }, /* 0xe30dc000 */
	{ TARGET_PCS10G_BR +  9,      0x30e0000, 1 }, /* 0xe30e0000 */
	{ TARGET_DEVRGMII,            0x30e4000, 1 }, /* 0xe30e4000 */
	{ TARGET_DEVRGMII +  1,       0x30e8000, 1 }, /* 0xe30e8000 */
	{ TARGET_DSM,                 0x30ec000, 1 }, /* 0xe30ec000 */
	{ TARGET_PORT_CONF,           0x30f0000, 1 }, /* 0xe30f0000 */
	{ TARGET_ASM,                 0x3200000, 1 }, /* 0xe3200000 */
	{ TARGET_HSIO_WRAP,           0x3408000, 1 }, /* 0xe3408000 */
};

static struct sparx5_sdlb_group lan969x_sdlb_groups[LAN969X_SDLB_GRP_CNT] = {
@@ -329,6 +332,7 @@ static const struct sparx5_ops lan969x_ops = {
	.is_port_5g              = &lan969x_port_is_5g,
	.is_port_10g             = &lan969x_port_is_10g,
	.is_port_25g             = &lan969x_port_is_25g,
	.is_port_rgmii           = &lan969x_port_is_rgmii,
	.get_port_dev_index      = &lan969x_port_dev_mapping,
	.get_port_dev_bit        = &lan969x_get_dev_mode_bit,
	.get_hsch_max_group_rate = &lan969x_get_hsch_max_group_rate,
@@ -336,6 +340,7 @@ static const struct sparx5_ops lan969x_ops = {
	.set_port_mux            = &lan969x_port_mux_set,
	.ptp_irq_handler         = &lan969x_ptp_irq_handler,
	.dsm_calendar_calc       = &lan969x_dsm_calendar_calc,
	.port_config_rgmii       = &lan969x_port_config_rgmii,
};

const struct sparx5_match_data lan969x_desc = {
+10 −0
Original line number Diff line number Diff line
@@ -59,7 +59,17 @@ static inline bool lan969x_port_is_25g(int portno)
	return false;
}

static inline bool lan969x_port_is_rgmii(int portno)
{
	return portno == 28 || portno == 29;
}

/* lan969x_calendar.c */
int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
			      struct sparx5_calendar_data *data);

/* lan969x_rgmii.c */
int lan969x_port_config_rgmii(struct sparx5_port *port,
			      struct sparx5_port_config *conf);

#endif
+224 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0+
/* Microchip lan969x Switch driver
 *
 * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
 */

#include "lan969x.h"

/* Tx clock selectors */
#define LAN969X_RGMII_TX_CLK_SEL_125MHZ 1  /* 1000Mbps */
#define LAN969X_RGMII_TX_CLK_SEL_25MHZ  2  /* 100Mbps */
#define LAN969X_RGMII_TX_CLK_SEL_2M5MHZ 3  /* 10Mbps */

/* Port speed selectors */
#define LAN969X_RGMII_SPEED_SEL_10 0   /* Select 10Mbps speed */
#define LAN969X_RGMII_SPEED_SEL_100 1  /* Select 100Mbps speed */
#define LAN969X_RGMII_SPEED_SEL_1000 2 /* Select 1000Mbps speed */

/* Clock delay selectors */
#define LAN969X_RGMII_CLK_DELAY_SEL_1_0_NS 2  /* Phase shift 45deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_1_7_NS 3  /* Phase shift 77deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_2_0_NS 4  /* Phase shift 90deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_2_5_NS 5  /* Phase shift 112deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_3_0_NS 6  /* Phase shift 135deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_3_3_NS 7  /* Phase shift 147deg */

#define LAN969X_RGMII_PORT_START_IDX 28 /* Index of the first RGMII port */
#define LAN969X_RGMII_IFG_TX 4          /* TX Inter Frame Gap value */
#define LAN969X_RGMII_IFG_RX1 5         /* RX1 Inter Frame Gap value */
#define LAN969X_RGMII_IFG_RX2 1         /* RX2 Inter Frame Gap value */

#define RGMII_PORT_IDX(port) ((port)->portno - LAN969X_RGMII_PORT_START_IDX)

/* Get the tx clock selector based on the port speed. */
static int lan969x_rgmii_get_clk_sel(int speed)
{
	return (speed == SPEED_10  ? LAN969X_RGMII_TX_CLK_SEL_2M5MHZ :
		speed == SPEED_100 ? LAN969X_RGMII_TX_CLK_SEL_25MHZ :
				     LAN969X_RGMII_TX_CLK_SEL_125MHZ);
}

/* Get the port speed selector based on the port speed. */
static int lan969x_rgmii_get_speed_sel(int speed)
{
	return (speed == SPEED_10  ? LAN969X_RGMII_SPEED_SEL_10 :
		speed == SPEED_100 ? LAN969X_RGMII_SPEED_SEL_100 :
				     LAN969X_RGMII_SPEED_SEL_1000);
}

/* Get the clock delay selector based on the clock delay in picoseconds. */
static int lan969x_rgmii_get_clk_delay_sel(struct sparx5_port *port,
					   u32 delay_ps, u32 *clk_delay_sel)
{
	switch (delay_ps) {
	case 0:
		/* Hardware default selector. */
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_2_5_NS;
		break;
	case 1000:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_1_0_NS;
		break;
	case 1700:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_1_7_NS;
		break;
	case 2000:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_2_0_NS;
		break;
	case 2500:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_2_5_NS;
		break;
	case 3000:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_3_0_NS;
		break;
	case 3300:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_3_3_NS;
		break;
	default:
		dev_err(port->sparx5->dev, "Invalid RGMII delay: %u", delay_ps);
		return -EINVAL;
	}

	return 0;
}

/* Configure the RGMII tx clock frequency. */
static void lan969x_rgmii_tx_clk_config(struct sparx5_port *port,
					struct sparx5_port_config *conf)
{
	u32 clk_sel = lan969x_rgmii_get_clk_sel(conf->speed);
	u32 idx = RGMII_PORT_IDX(port);

	/* Take the RGMII clock domain out of reset and set tx clock
	 * frequency.
	 */
	spx5_rmw(HSIO_WRAP_RGMII_CFG_TX_CLK_CFG_SET(clk_sel) |
		 HSIO_WRAP_RGMII_CFG_RGMII_TX_RST_SET(0) |
		 HSIO_WRAP_RGMII_CFG_RGMII_RX_RST_SET(0),
		 HSIO_WRAP_RGMII_CFG_TX_CLK_CFG |
		 HSIO_WRAP_RGMII_CFG_RGMII_TX_RST |
		 HSIO_WRAP_RGMII_CFG_RGMII_RX_RST,
		 port->sparx5, HSIO_WRAP_RGMII_CFG(idx));
}

/* Configure the RGMII port device. */
static void lan969x_rgmii_port_device_config(struct sparx5_port *port,
					     struct sparx5_port_config *conf)
{
	u32 dtag, dotag, etype, speed_sel, idx = RGMII_PORT_IDX(port);

	speed_sel = lan969x_rgmii_get_speed_sel(conf->speed);

	etype = (port->vlan_type == SPX5_VLAN_PORT_TYPE_S_CUSTOM ?
		 port->custom_etype :
		 port->vlan_type == SPX5_VLAN_PORT_TYPE_C ?
		 ETH_P_8021Q : ETH_P_8021AD);

	dtag = port->max_vlan_tags == SPX5_PORT_MAX_TAGS_TWO;
	dotag = port->max_vlan_tags != SPX5_PORT_MAX_TAGS_NONE;

	/* Enable the MAC. */
	spx5_wr(DEVRGMII_MAC_ENA_CFG_RX_ENA_SET(1) |
		DEVRGMII_MAC_ENA_CFG_TX_ENA_SET(1),
		port->sparx5, DEVRGMII_MAC_ENA_CFG(idx));

	/* Configure the Inter Frame Gap. */
	spx5_wr(DEVRGMII_MAC_IFG_CFG_TX_IFG_SET(LAN969X_RGMII_IFG_TX) |
		DEVRGMII_MAC_IFG_CFG_RX_IFG1_SET(LAN969X_RGMII_IFG_RX1) |
		DEVRGMII_MAC_IFG_CFG_RX_IFG2_SET(LAN969X_RGMII_IFG_RX2),
		port->sparx5, DEVRGMII_MAC_IFG_CFG(idx));

	/* Configure port data rate. */
	spx5_wr(DEVRGMII_DEV_RST_CTRL_SPEED_SEL_SET(speed_sel),
		port->sparx5, DEVRGMII_DEV_RST_CTRL(idx));

	/* Configure VLAN awareness. */
	spx5_wr(DEVRGMII_MAC_TAGS_CFG_TAG_ID_SET(etype) |
		DEVRGMII_MAC_TAGS_CFG_PB_ENA_SET(dtag) |
		DEVRGMII_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(dotag) |
		DEVRGMII_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_SET(dotag),
		port->sparx5,
		DEVRGMII_MAC_TAGS_CFG(idx));
}

/* Configure the RGMII delay lines in the MAC.
 *
 * We use the rx-internal-delay-ps" and "tx-internal-delay-ps" properties to
 * configure the rx and tx delays for the MAC. If these properties are missing
 * or set to zero, the MAC will not apply any delay.
 *
 * The PHY side delays are determined by the PHY mode
 * (e.g. PHY_INTERFACE_MODE_RGMII_{ID, RXID, TXID}), and ignored by the MAC side
 * entirely.
 */
static int lan969x_rgmii_delay_config(struct sparx5_port *port,
				      struct sparx5_port_config *conf)
{
	u32 tx_clk_sel, rx_clk_sel, tx_delay_ps = 0, rx_delay_ps = 0;
	u32 idx = RGMII_PORT_IDX(port);
	int err;

	of_property_read_u32(port->of_node, "rx-internal-delay-ps",
			     &rx_delay_ps);

	of_property_read_u32(port->of_node, "tx-internal-delay-ps",
			     &tx_delay_ps);

	err = lan969x_rgmii_get_clk_delay_sel(port, rx_delay_ps, &rx_clk_sel);
	if (err)
		return err;

	err = lan969x_rgmii_get_clk_delay_sel(port, tx_delay_ps, &tx_clk_sel);
	if (err)
		return err;

	/* Configure rx delay. */
	spx5_rmw(HSIO_WRAP_DLL_CFG_DLL_RST_SET(0) |
		 HSIO_WRAP_DLL_CFG_DLL_ENA_SET(1) |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_ENA_SET(!!rx_delay_ps) |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_SEL_SET(rx_clk_sel),
		 HSIO_WRAP_DLL_CFG_DLL_RST |
		 HSIO_WRAP_DLL_CFG_DLL_ENA |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_ENA |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_SEL,
		 port->sparx5, HSIO_WRAP_DLL_CFG(idx, 0));

	/* Configure tx delay. */
	spx5_rmw(HSIO_WRAP_DLL_CFG_DLL_RST_SET(0) |
		 HSIO_WRAP_DLL_CFG_DLL_ENA_SET(1) |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_ENA_SET(!!tx_delay_ps) |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_SEL_SET(tx_clk_sel),
		 HSIO_WRAP_DLL_CFG_DLL_RST |
		 HSIO_WRAP_DLL_CFG_DLL_ENA |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_ENA |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_SEL,
		 port->sparx5, HSIO_WRAP_DLL_CFG(idx, 1));

	return 0;
}

/* Configure GPIO's to be used as RGMII interface. */
static void lan969x_rgmii_gpio_config(struct sparx5_port *port)
{
	u32 idx = RGMII_PORT_IDX(port);

	/* Enable the RGMII on the GPIOs. */
	spx5_wr(HSIO_WRAP_XMII_CFG_GPIO_XMII_CFG_SET(1), port->sparx5,
		HSIO_WRAP_XMII_CFG(!idx));
}

int lan969x_port_config_rgmii(struct sparx5_port *port,
			      struct sparx5_port_config *conf)
{
	int err;

	err = lan969x_rgmii_delay_config(port, conf);
	if (err)
		return err;

	lan969x_rgmii_tx_clk_config(port, conf);
	lan969x_rgmii_gpio_config(port);
	lan969x_rgmii_port_device_config(port, conf);

	return 0;
}
Loading