Skip to content

Commit

Permalink
Merge branch 'net-lan969x-add-rgmii-support'
Browse files Browse the repository at this point in the history
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: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
kuba-moo committed Dec 23, 2024
2 parents 847cf3b + f0706c0 commit 9268abe
Show file tree
Hide file tree
Showing 11 changed files with 484 additions and 29 deletions.
18 changes: 18 additions & 0 deletions Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/ethernet/microchip/sparx5/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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] = {
Expand Down Expand Up @@ -329,13 +332,15 @@ 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,
.get_sdlb_group = &lan969x_get_sdlb_group,
.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 = {
Expand Down
10 changes: 10 additions & 0 deletions drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 224 additions & 0 deletions drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_rgmii.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// 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;
}
29 changes: 21 additions & 8 deletions drivers/net/ethernet/microchip/sparx5/sparx5_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,10 +313,13 @@ static int sparx5_create_port(struct sparx5 *sparx5,
struct initial_port_config *config)
{
struct sparx5_port *spx5_port;
const struct sparx5_ops *ops;
struct net_device *ndev;
struct phylink *phylink;
int err;

ops = sparx5->data->ops;

ndev = sparx5_create_netdev(sparx5, config->portno);
if (IS_ERR(ndev)) {
dev_err(sparx5->dev, "Could not create net device: %02u\n",
Expand Down Expand Up @@ -357,6 +360,9 @@ static int sparx5_create_port(struct sparx5 *sparx5,
MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD |
MAC_2500FD | MAC_5000FD | MAC_10000FD | MAC_25000FD;

if (ops->is_port_rgmii(spx5_port->portno))
phy_interface_set_rgmii(spx5_port->phylink_config.supported_interfaces);

__set_bit(PHY_INTERFACE_MODE_SGMII,
spx5_port->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_QSGMII,
Expand Down Expand Up @@ -830,6 +836,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
struct initial_port_config *configs, *config;
struct device_node *np = pdev->dev.of_node;
struct device_node *ports, *portnp;
const struct sparx5_ops *ops;
struct reset_control *reset;
struct sparx5 *sparx5;
int idx = 0, err = 0;
Expand All @@ -851,6 +858,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
return -EINVAL;

regs = sparx5->data->regs;
ops = sparx5->data->ops;

/* Do switch core reset if available */
reset = devm_reset_control_get_optional_shared(&pdev->dev, "switch");
Expand Down Expand Up @@ -880,7 +888,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)

for_each_available_child_of_node(ports, portnp) {
struct sparx5_port_config *conf;
struct phy *serdes;
struct phy *serdes = NULL;
u32 portno;

err = of_property_read_u32(portnp, "reg", &portno);
Expand Down Expand Up @@ -910,13 +918,17 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
conf->sd_sgpio = ~0;
else
sparx5->sd_sgpio_remapping = true;
serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
if (IS_ERR(serdes)) {
err = dev_err_probe(sparx5->dev, PTR_ERR(serdes),
"port %u: missing serdes\n",
portno);
of_node_put(portnp);
goto cleanup_config;
/* There is no SerDes node for RGMII ports. */
if (!ops->is_port_rgmii(portno)) {
serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
if (IS_ERR(serdes)) {
err = dev_err_probe(sparx5->dev,
PTR_ERR(serdes),
"port %u: missing serdes\n",
portno);
of_node_put(portnp);
goto cleanup_config;
}
}
config->portno = portno;
config->node = portnp;
Expand Down Expand Up @@ -1072,6 +1084,7 @@ static const struct sparx5_ops sparx5_ops = {
.is_port_5g = &sparx5_port_is_5g,
.is_port_10g = &sparx5_port_is_10g,
.is_port_25g = &sparx5_port_is_25g,
.is_port_rgmii = &sparx5_port_is_rgmii,
.get_port_dev_index = &sparx5_port_dev_mapping,
.get_port_dev_bit = &sparx5_port_dev_mapping,
.get_hsch_max_group_rate = &sparx5_get_hsch_max_group_rate,
Expand Down
Loading

0 comments on commit 9268abe

Please sign in to comment.