lx2162-clearfog: add support for 25Gbps on 2x SFP+ connectors

Ensure to choose serdes 1 protocol 18 to enable support for 25Gbpps on 2
of the SFP+ connectors. The other 2 do not have a retimer and will
likely have bad signal integrity if used at 25Gbpbs.
This commit is contained in:
Josua Mayer 2023-04-17 14:03:35 +03:00
parent 7fbc7465e0
commit 7d929bd639
4 changed files with 559 additions and 0 deletions

View File

@ -85,3 +85,4 @@ CONFIG_IPVLAN=y
CONFIG_CGROUPS=y
CONFIG_NAMESPACES=y
CONFIG_NET_NS=y
CONFIG_PHY_DS250DFX10=y

View File

@ -0,0 +1,424 @@
From 7fe34a8f3c05b7ffef115dfab90afd4e124299b6 Mon Sep 17 00:00:00 2001
From: Josua Mayer <josua@solid-run.com>
Date: Wed, 12 Oct 2022 18:46:09 +0300
Subject: [PATCH 62/63] net: phy: create driver for ds250df4x10 retimer
Create driver for the TI DS250DF410 and DS250DF810 retimers.
Both variants feature either 4 or 8 channels to be configured
individually from 20.2752 to 25.8Gbps, while also supporting subrates by
dividing either with 2 or 4.
Configuration is provided to other drivers by implementing a generic phy
with support for set_mode.
For now only 2 of the standard configurations are supported, to support
10 & 25Gbps ethernet modes:
- PHY_INTERFACE_MODE_10GBASER: 10.3125Gbps
- PHY_INTERFACE_MODE_25GBASER: 25.78125Gbps
The driver also hardcodes signal conditioning parameters.
Future revisions shall read those from device-tree instead.
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
drivers/phy/ti/Kconfig | 9 +
drivers/phy/ti/Makefile | 1 +
drivers/phy/ti/phy-ti-ds250dfx10.c | 354 +++++++++++++++++++++++++++++
3 files changed, 364 insertions(+)
create mode 100644 drivers/phy/ti/phy-ti-ds250dfx10.c
diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig
index 15a3bcf32308..7117e819cdfd 100644
--- a/drivers/phy/ti/Kconfig
+++ b/drivers/phy/ti/Kconfig
@@ -84,6 +84,15 @@ config TI_PIPE3
This driver interacts with the "OMAP Control PHY Driver" to power
on/off the PHY.
+config PHY_DS250DFX10
+ tristate "Texas Instruments DS250DFX10 Retimer"
+ depends on OF
+ select GENERIC_PHY
+ help
+ Enable to support runtime configuration of DS250DFX10 retimers.
+ The retimers are modeled as generic PHYs,
+ currently supporting 10 & 25 GBASER link speeds.
+
config PHY_TUSB1210
tristate "TI TUSB1210 ULPI PHY module"
depends on USB_ULPI_BUS
diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile
index dcba2571c9bd..718db2aadcdd 100644
--- a/drivers/phy/ti/Makefile
+++ b/drivers/phy/ti/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
+obj-$(CONFIG_PHY_DS250DFX10) += phy-ti-ds250dfx10.o
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
obj-$(CONFIG_PHY_AM654_SERDES) += phy-am654-serdes.o
diff --git a/drivers/phy/ti/phy-ti-ds250dfx10.c b/drivers/phy/ti/phy-ti-ds250dfx10.c
new file mode 100644
index 000000000000..bfbbf35c430c
--- /dev/null
+++ b/drivers/phy/ti/phy-ti-ds250dfx10.c
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the TI DS250DF410 Retimer
+ *
+ * Copyright (C) 2022-2023 Josua Mayer <josua@solid-run.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/phy/phy.h>
+
+#define DS250DF410_REG_CHAN_CONFIG_ID 0xEF
+#define DS250DF410_MASK_CHAN_CONFIG_ID GENMASK(3, 0)
+#define DS250DF410_REG_VERSION 0xF0
+#define DS250DF410_REG_DEVICE_ID 0xF1
+#define DS250DF410_REG_CHAN_VERSION 0xF3
+#define DS250DF410_MASK_CHAN_VERSION GENMASK(7, 4)
+#define DS250DF410_MASK_SHARE_VERSION GENMASK(3, 0)
+
+struct ds250dfx10_phy_priv {
+ struct i2c_client *client;
+ uint8_t channel;
+};
+
+struct ds250dfx10_priv {
+ struct phy *phy[8];
+ struct phy_provider *provider;
+};
+
+static int ds250dfx10_read_register(struct i2c_client *client, uint8_t address, uint8_t *value,
+ uint8_t mask)
+{
+ s32 res;
+
+ res = i2c_smbus_read_byte_data(client, address);
+ if (res < 0) {
+ dev_err(&client->dev, "failed to read register %#04x: %d\n", address,
+ res);
+ return -EIO;
+ }
+
+ *value = res & mask;
+ return 0;
+}
+
+static int ds250dfx10_write_register(struct i2c_client *client, uint8_t address, uint8_t value,
+ uint8_t mask)
+{
+ int ret;
+ uint8_t tmp;
+ s32 res;
+
+ // combine with current value according to mask
+ if (mask != 0xFF) {
+ ret = ds250dfx10_read_register(client, address, &tmp, ~mask);
+ if (ret)
+ return ret;
+
+ value = (value & mask) | tmp;
+ }
+
+ // write new value
+ res = i2c_smbus_write_byte_data(client, address, value);
+ if (res < 0) {
+ dev_err(&client->dev, "failed to write register %#04x=%#04x: %d\n",
+ address, value, res);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void ds250dfx10_config_10g(struct i2c_client *client, uint8_t channel)
+{
+ int ret = 0;
+
+ // enable smbus access to single channel
+ ret |= ds250dfx10_write_register(client, 0xFF, 0x01, 0x03);
+
+ // select channel
+ ret |= ds250dfx10_write_register(client, 0xFC, 1 << channel, 0xFF);
+
+ // reset channel registers
+ ret |= ds250dfx10_write_register(client, 0x00, 0x04, 0x04);
+
+ // assert cdr
+ ret |= ds250dfx10_write_register(client, 0x0A, 0x0C, 0x0C);
+
+ // select 10.3125 rate
+ ret |= ds250dfx10_write_register(client, 0x2F, 0x00, 0xF0);
+
+ // enable pre- and post-fir
+ ret |= ds250dfx10_write_register(client, 0x3D, 0x80, 0x80);
+
+ // set main cursor magnitude +15
+ ret |= ds250dfx10_write_register(client, 0x3D, 0x00, 0x40);
+ ret |= ds250dfx10_write_register(client, 0x3D, 0x0F, 0x1F);
+
+ // set pre cursor magnitude -4
+ ret |= ds250dfx10_write_register(client, 0x3E, 0x40, 0x40);
+ ret |= ds250dfx10_write_register(client, 0x3E, 0x04, 0x0F);
+
+ // set post cursor magnitude -4
+ ret |= ds250dfx10_write_register(client, 0x3F, 0x40, 0x40);
+ ret |= ds250dfx10_write_register(client, 0x3F, 0x04, 0x0F);
+
+ // deassert cdr
+ ret |= ds250dfx10_write_register(client, 0x0A, 0x00, 0x0C);
+
+ if (!ret)
+ dev_info(&client->dev, "configured channel %u for 10G\n", channel);
+}
+
+static void ds250dfx10_config_25g(struct i2c_client *client, uint8_t channel)
+{
+ int ret = 0;
+
+ // enable smbus access to single channel
+ ret |= ds250dfx10_write_register(client, 0xFF, 0x01, 0x03);
+
+ // select channel
+ ret |= ds250dfx10_write_register(client, 0xFC, 1 << channel, 0xFF);
+
+ // reset channel registers
+ ret |= ds250dfx10_write_register(client, 0x00, 0x04, 0x04);
+
+ // assert cdr
+ ret |= ds250dfx10_write_register(client, 0x0A, 0x0C, 0x0C);
+
+ // select 25.78125 rate
+ ret |= ds250dfx10_write_register(client, 0x2F, 0x50, 0xF0);
+
+ // enable pre- and post-fir
+ ret |= ds250dfx10_write_register(client, 0x3D, 0x80, 0x80);
+
+ // set main cursor magnitude +15
+ ret |= ds250dfx10_write_register(client, 0x3D, 0x00, 0x40);
+ ret |= ds250dfx10_write_register(client, 0x3D, 0x0F, 0x1F);
+
+ // set pre cursor magnitude -4
+ ret |= ds250dfx10_write_register(client, 0x3E, 0x40, 0x40);
+ ret |= ds250dfx10_write_register(client, 0x3E, 0x04, 0x0F);
+
+ // set post cursor magnitude -4
+ ret |= ds250dfx10_write_register(client, 0x3F, 0x40, 0x40);
+ ret |= ds250dfx10_write_register(client, 0x3F, 0x04, 0x0F);
+
+ // deassert cdr
+ ret |= ds250dfx10_write_register(client, 0x0A, 0x00, 0x0C);
+
+ if (!ret)
+ dev_info(&client->dev, "configured channel %u for 25G\n", channel);
+}
+
+static int ds250dfx10_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct ds250dfx10_phy_priv *priv = phy_get_drvdata(phy);
+
+ if (mode != PHY_MODE_ETHERNET)
+ return -EOPNOTSUPP;
+
+ switch (submode) {
+ case PHY_INTERFACE_MODE_10GBASER:
+ ds250dfx10_config_10g(priv->client, priv->channel);
+ break;
+ case PHY_INTERFACE_MODE_25GBASER:
+ ds250dfx10_config_25g(priv->client, priv->channel);
+ break;
+ default:
+ dev_err(&priv->client->dev, "unsupported interface submode %i\n",
+ submode);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static const struct phy_ops ds250dfx10_phy_ops = {
+ .set_mode = ds250dfx10_phy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *ds250dfx10_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+ struct ds250dfx10_priv *phy_priv = dev_get_drvdata(dev);
+ int channel;
+
+ if (args->args_count != 1) {
+ dev_err(phy_priv->provider->dev, "DT did not pass correct no of args\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ channel = args->args[0];
+ if (WARN_ON(channel >= ARRAY_SIZE(phy_priv->phy))
+ || !phy_priv->phy[channel])
+ return ERR_PTR(-ENODEV);
+
+ return phy_priv->phy[channel];
+}
+
+static int ds250dfx10_probe(struct i2c_client *client)
+{
+ struct ds250dfx10_priv *priv;
+ struct ds250dfx10_phy_priv *phy_priv;
+ struct device_node *child;
+ uint8_t chan_config_id, device_id, version, chan_version, share_version, channels;
+ uint8_t reg;
+ int ret, i;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto no_phys;
+ }
+ i2c_set_clientdata(client, priv);
+
+ /* read device identification */
+ ret = ds250dfx10_read_register(client, DS250DF410_REG_DEVICE_ID, &reg, 0xFF);
+ if (ret)
+ goto no_phys;
+ device_id = reg;
+
+ /* read device version */
+ ret = ds250dfx10_read_register(client, DS250DF410_REG_VERSION, &reg, 0xFF);
+ if (ret)
+ goto no_phys;
+ version = reg;
+
+ // report device id
+ dev_info(&client->dev, "device id %#04x version %#04x\n", device_id, version);
+
+ switch (device_id) {
+ case 0x10:
+ break;
+ default:
+ dev_warn(&client->dev, "unknown device id, expect problems!\n");
+ }
+
+ // read channel config id
+ ret = ds250dfx10_read_register(client, DS250DF410_REG_CHAN_CONFIG_ID, &reg,
+ DS250DF410_MASK_CHAN_CONFIG_ID);
+ if (ret)
+ goto no_phys;
+ chan_config_id = reg;
+
+ switch (chan_config_id) {
+ case 0xC:
+ channels = 8;
+ break;
+ case 0xE:
+ channels = 4;
+ break;
+ default:
+ dev_err(&client->dev, "unknown channel configuration id %#03x\n", chan_config_id);
+ ret = -EINVAL;
+ goto no_phys;
+ }
+ dev_info(&client->dev, "%u channels\n", channels);
+
+ // read channel version
+ ret = ds250dfx10_read_register(client, DS250DF410_REG_CHAN_VERSION, &reg, 0xFF);
+ if (ret)
+ goto no_phys;
+ chan_version = (reg & DS250DF410_MASK_CHAN_VERSION) >> 4;
+ share_version = reg & DS250DF410_MASK_SHARE_VERSION;
+
+ dev_info(&client->dev, "channel version %#03x share version %#03x\n",
+ chan_version, share_version);
+
+ // create PHY objects for all channels
+ for (i = 0; i < channels; i++) {
+ priv->phy[i] = devm_phy_create(&client->dev, child, &ds250dfx10_phy_ops);
+ if (IS_ERR(priv->phy[i])) {
+ ret = PTR_ERR(priv->phy[i]);
+ priv->phy[i] = NULL;
+ of_node_put(child);
+ goto no_provider;
+ }
+
+ phy_priv = devm_kzalloc(&client->dev, sizeof(*phy_priv), GFP_KERNEL);
+ if (!phy_priv) {
+ ret = -ENOMEM;
+ goto no_provider;
+ }
+ phy_set_drvdata(priv->phy[i], phy_priv);
+
+ phy_priv->client = client;
+ phy_priv->channel = i;
+
+ dev_info(&client->dev, "created phy for channel %u\n", i);
+ }
+ of_node_put(child);
+
+ // register self as phy provider with generic lookup function
+ priv->provider = devm_of_phy_provider_register(&client->dev, ds250dfx10_of_xlate);
+
+ return 0;
+
+ devm_of_phy_provider_unregister(&client->dev, priv->provider);
+no_provider:
+ for (i = 0; i < 8; i++) {
+ if (priv->phy[i])
+ devm_phy_destroy(&client->dev, priv->phy[i]);
+ }
+no_phys:
+ return ret;
+}
+
+static int ds250dfx10_remove(struct i2c_client *client)
+{
+ struct ds250dfx10_priv *priv = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (priv->phy[i])
+ devm_phy_destroy(&client->dev, priv->phy[i]);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ds250dfx10_dt_ids[] = {
+ { .compatible = "ti,ds250df410", },
+ { .compatible = "ti,ds250df810", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ds250dfx10_dt_ids);
+#endif
+
+static struct i2c_device_id ds250dfx10_idtable[] = {
+ { "ds250df410", 0 },
+ { "ds250df810", 1 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, ds250dfx10_idtable);
+
+static struct i2c_driver ds250dfx10_driver = {
+ .driver = {
+ .name = "ds250dfx10",
+ .of_match_table = of_match_ptr(ds250dfx10_dt_ids),
+ },
+
+ .id_table = ds250dfx10_idtable,
+ .probe_new = ds250dfx10_probe,
+ .remove = ds250dfx10_remove,
+};
+
+module_i2c_driver(ds250dfx10_driver);
+
+MODULE_AUTHOR("Josua Mayer <josua@solid-run.com>");
+MODULE_DESCRIPTION("TI DS250DFX10 Retimer Driver");
+MODULE_LICENSE("GPL");
--
2.35.3

View File

@ -0,0 +1,126 @@
From f54f49af359f8208f2ba1780ec6ac8ef587310c9 Mon Sep 17 00:00:00 2001
From: Josua Mayer <josua@solid-run.com>
Date: Sun, 16 Apr 2023 13:24:31 +0300
Subject: [PATCH 63/63] arm64: dts: lx2162-clearfog: add description for
retimer
LX2162 Clearfog has a retimer on 2x SFP+ connectors.
Add a node for the retimer and link it to the appropriate mac nodes.
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
.../dts/freescale/fsl-lx2162a-clearfog.dts | 20 +++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts
index 961c9a6dac0f..25975bb64f12 100644
--- a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts
@@ -75,9 +75,10 @@ sfp_bb: sfp-bb {
};
&i2c2 {
- retimer@30 {
+ retimer: ds250df410@18 {
compatible = "ti,ds250df410";
- reg = <0x30>;
+ reg = <0x18>;
+ #phy-cells = <1>;
};
i2c-switch@70 {
@@ -191,6 +192,7 @@ pcieclk@6b {
&dpmac3 {
status = "okay";
phys = <&serdes_1 7>;
+ phy-names = "serdes";
managed = "in-band-status";
sfp = <&sfp_at>;
link-status-led = <&led_sfp_at>;
@@ -204,6 +206,7 @@ &pcs_mdio3 {
&dpmac4 {
status = "okay";
phys = <&serdes_1 6>;
+ phy-names = "serdes";
managed = "in-band-status";
sfp = <&sfp_ab>;
link-status-led = <&led_sfp_ab>;
@@ -215,7 +218,8 @@ &pcs_mdio4 {
&dpmac5 {
status = "okay";
- phys = <&serdes_1 5>;
+ phys = <&serdes_1 5>, <&retimer 2>, <&retimer 3>;
+ phy-names = "serdes", "retimer", "retimer";
managed = "in-band-status";
sfp = <&sfp_bt>;
link-status-led = <&led_sfp_bt>;
@@ -227,7 +231,8 @@ &pcs_mdio5 {
&dpmac6 {
status = "okay";
- phys = <&serdes_1 4>;
+ phys = <&serdes_1 4>, <&retimer 0>, <&retimer 1>;
+ phy-names = "serdes", "retimer", "retimer";
managed = "in-band-status";
sfp = <&sfp_bb>;
link-status-led = <&led_sfp_bb>;
@@ -300,6 +305,7 @@ &pcs_mdio11 {
&dpmac12 {
status = "okay";
phys = <&serdes_2 1>;
+ phy-names = "serdes";
phy-handle = <&ethernet_phy0>;
phy-mode = "rgmii";
};
@@ -315,6 +321,7 @@ &dpmac17 {
status = "okay";
phys = <&serdes_2 2>;
+ phy-names = "serdes";
phy-handle = <&ethernet_phy4>;
phy-mode = "rgmii";
};
@@ -326,6 +333,7 @@ &pcs_mdio17 {
&dpmac18 {
status = "okay";
phys = <&serdes_2 3>;
+ phy-names = "serdes";
phy-handle = <&ethernet_phy6>;
phy-mode = "rgmii";
};
@@ -337,6 +345,7 @@ &pcs_mdio18 {
&dpmac15 {
status = "okay";
phys = <&serdes_2 4>;
+ phy-names = "serdes";
phy-handle = <&ethernet_phy3>;
phy-mode = "rgmii";
};
@@ -348,6 +357,7 @@ &pcs_mdio15 {
&dpmac16 {
status = "okay";
phys = <&serdes_2 5>;
+ phy-names = "serdes";
phy-handle = <&ethernet_phy1>;
phy-mode = "rgmii";
};
@@ -359,6 +369,7 @@ &pcs_mdio16 {
&dpmac13 {
status = "okay";
phys = <&serdes_2 6>;
+ phy-names = "serdes";
phy-handle = <&ethernet_phy5>;
phy-mode = "rgmii";
};
@@ -370,6 +381,7 @@ &pcs_mdio13 {
&dpmac14 {
status = "okay";
phys = <&serdes_2 7>;
+ phy-names = "serdes";
phy-handle = <&ethernet_phy7>;
phy-mode = "rgmii";
};
--
2.35.3

View File

@ -152,11 +152,19 @@ case "${SERDES}" in
DPC=LX2162-USOM/clearfog-s1_3-s2_0-dpc.dtb
DPL=LX2162-USOM/clearfog-s1_3-s2_0-dpl.dtb
DEFAULT_FDT_FILE="fsl-lx2162a-clearfog.dtb"
MC_FORCE=patches/mc_lx2160a_10.32.0.itb
;;
LX2162A_CLEARFOG_3_9_*)
DPC=LX2162-USOM/clearfog-s1_3-s2_9-dpc.dtb
DPL=LX2162-USOM/clearfog-s1_3-s2_9-dpl.dtb
DEFAULT_FDT_FILE="fsl-lx2162a-clearfog.dtb"
MC_FORCE=patches/mc_lx2160a_10.32.0.itb
;;
LX2162A_CLEARFOG_18_0_*)
DPC=LX2162-USOM/clearfog-s1_3-s2_0-dpc.dtb
DPL=LX2162-USOM/clearfog-s1_3-s2_0-dpl.dtb
DEFAULT_FDT_FILE="fsl-lx2162a-clearfog.dtb"
MC_FORCE=patches/mc_lx2160a_10.36.100.itb
;;
*)
echo "Please define SERDES configuration"