From 36755077b3cb222b1f6c4e87296f8530f122d21a Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 31 Oct 2024 15:24:09 +0100 Subject: [PATCH] linux: support lx2162 clearfog retimer on 25G sfp ports --- configs/linux/lx2k_additions.config | 2 + .../0020-phy-add-of_phy_get_by_index.patch | 79 ++++ ...t-dpaa2-add-support-for-retimer-phys.patch | 212 +++++++++ ...reate-driver-for-ds250df4x10-retimer.patch | 424 ++++++++++++++++++ ...-clearfog-add-description-for-retime.patch | 52 +++ 5 files changed, 769 insertions(+) create mode 100644 patches/linux/0020-phy-add-of_phy_get_by_index.patch create mode 100644 patches/linux/0021-net-dpaa2-add-support-for-retimer-phys.patch create mode 100644 patches/linux/0022-net-phy-create-driver-for-ds250df4x10-retimer.patch create mode 100644 patches/linux/0023-arm64-dts-lx2162-clearfog-add-description-for-retime.patch diff --git a/configs/linux/lx2k_additions.config b/configs/linux/lx2k_additions.config index 9a2f2b7..fa6d4bb 100644 --- a/configs/linux/lx2k_additions.config +++ b/configs/linux/lx2k_additions.config @@ -24,3 +24,5 @@ CONFIG_SFP=y # lx2162 clearfog # c45 phy driver modules don't auto-load ... CONFIG_MARVELL_10G_PHY=y +# confuses netdev order if loaded late (module) +CONFIG_PHY_DS250DFX10=y diff --git a/patches/linux/0020-phy-add-of_phy_get_by_index.patch b/patches/linux/0020-phy-add-of_phy_get_by_index.patch new file mode 100644 index 0000000..c95d1e5 --- /dev/null +++ b/patches/linux/0020-phy-add-of_phy_get_by_index.patch @@ -0,0 +1,79 @@ +From 761fde5d51c7c0f26f12a684d479836a9590f9ea Mon Sep 17 00:00:00 2001 +From: Josua Mayer +Date: Wed, 23 Nov 2022 13:23:03 +0200 +Subject: [PATCH 20/23] phy: add of_phy_get_by_index + +Support getting a phy by index directly. +This is mostly useful for hardware specifying multiple similar +generic phys using the "phys" and "phy-names" properties. + +Current user is dpaa2 driver that can have a phy object for each serdes +lane, and additionally 2 retimer channels (tx&rx). + +Signed-off-by: Josua Mayer +--- + drivers/phy/phy-core.c | 11 +++++++++-- + include/linux/phy/phy.h | 6 ++++++ + 2 files changed, 15 insertions(+), 2 deletions(-) + +diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c +index 688b204fcf36..a0f6b5cbbaaa 100644 +--- a/drivers/phy/phy-core.c ++++ b/drivers/phy/phy-core.c +@@ -579,12 +579,19 @@ static struct phy *_of_phy_get(struct device_node *np, int index) + */ + struct phy *of_phy_get(struct device_node *np, const char *con_id) + { +- struct phy *phy = NULL; + int index = 0; + + if (con_id) + index = of_property_match_string(np, "phy-names", con_id); + ++ return of_phy_get_by_index(np, index); ++} ++EXPORT_SYMBOL_GPL(of_phy_get); ++ ++struct phy *of_phy_get_by_index(struct device_node *np, int index) ++{ ++ struct phy *phy = NULL; ++ + phy = _of_phy_get(np, index); + if (IS_ERR(phy)) + return phy; +@@ -596,7 +603,7 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id) + + return phy; + } +-EXPORT_SYMBOL_GPL(of_phy_get); ++EXPORT_SYMBOL_GPL(of_phy_get_by_index); + + /** + * of_phy_put() - release the PHY +diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h +index 980a2427cc33..300829b8bc62 100644 +--- a/include/linux/phy/phy.h ++++ b/include/linux/phy/phy.h +@@ -259,6 +259,7 @@ void of_phy_put(struct phy *phy); + void phy_put(struct device *dev, struct phy *phy); + void devm_phy_put(struct device *dev, struct phy *phy); + struct phy *of_phy_get(struct device_node *np, const char *con_id); ++struct phy *of_phy_get_by_index(struct device_node *np, int index); + struct phy *of_phy_simple_xlate(struct device *dev, + struct of_phandle_args *args); + struct phy *phy_create(struct device *dev, struct device_node *node, +@@ -472,6 +473,11 @@ static inline struct phy *of_phy_get(struct device_node *np, const char *con_id) + return ERR_PTR(-ENOSYS); + } + ++static inline struct phy *of_phy_get_by_index(struct device_node *np, int index) ++{ ++ return ERR_PTR(-ENOSYS); ++} ++ + static inline struct phy *of_phy_simple_xlate(struct device *dev, + struct of_phandle_args *args) + { +-- +2.43.0 + diff --git a/patches/linux/0021-net-dpaa2-add-support-for-retimer-phys.patch b/patches/linux/0021-net-dpaa2-add-support-for-retimer-phys.patch new file mode 100644 index 0000000..788d516 --- /dev/null +++ b/patches/linux/0021-net-dpaa2-add-support-for-retimer-phys.patch @@ -0,0 +1,212 @@ +From b6696f279d83e889c92a82bd1d3268d6b101a64e Mon Sep 17 00:00:00 2001 +From: Josua Mayer +Date: Sun, 16 Apr 2023 14:32:28 +0300 +Subject: [PATCH 21/23] net: dpaa2: add support for retimer phys + +To support network speeds greater than 10Gbps retimers may be connected +on tx & rx lines between the SoC and external phy. + +Add support for dynamic configuration of retimers modeles as generic phy +ojects, and reconfigure them on interface type change. + +In order to extract multiple different types of phy objects, i.e. serdes +& retimer, the existing code using of_phy_get is replaced by a loop +handling both serdes & retimer phys. + +In the future to support 40G & 100G interface speeds, the number of +supported phys might need to be increased from the current maximum of +one serdes and 2 retimer phys. + +Signed-off-by: Josua Mayer +--- + .../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 108 ++++++++++++++---- + .../net/ethernet/freescale/dpaa2/dpaa2-mac.h | 1 + + 2 files changed, 85 insertions(+), 24 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index 55f08c84b3dc..3d4c0f9206fb 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -166,6 +166,7 @@ static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode, + struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config); + struct dpmac_link_state *dpmac_state = &mac->state; + int err; ++ int i; + + if (state->an_enabled) + dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG; +@@ -178,18 +179,27 @@ static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode, + netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n", + __func__, err); + +- if (!mac->serdes_phy) +- return; ++ if (mac->features & DPAA2_MAC_FEATURE_PROTOCOL_CHANGE && mac->serdes_phy) { ++ /* This happens only if we support changing of protocol at runtime */ ++ err = dpmac_set_protocol(mac->mc_io, 0, mac->mc_dev->mc_handle, ++ dpmac_eth_if_mode(state->interface)); ++ if (err) ++ netdev_err(mac->net_dev, "dpmac_set_protocol() = %d\n", err); + +- /* This happens only if we support changing of protocol at runtime */ +- err = dpmac_set_protocol(mac->mc_io, 0, mac->mc_dev->mc_handle, +- dpmac_eth_if_mode(state->interface)); +- if (err) +- netdev_err(mac->net_dev, "dpmac_set_protocol() = %d\n", err); ++ err = phy_set_mode_ext(mac->serdes_phy, PHY_MODE_ETHERNET, state->interface); ++ if (err) ++ netdev_err(mac->net_dev, "phy_set_mode_ext() = %d\n", err); ++ } + +- err = phy_set_mode_ext(mac->serdes_phy, PHY_MODE_ETHERNET, state->interface); +- if (err) +- netdev_err(mac->net_dev, "phy_set_mode_ext() = %d\n", err); ++ /* always configure retimers */ ++ for (i = 0; i < sizeof(mac->retimer_phys)/sizeof(mac->retimer_phys[0]); i++) { ++ if (!mac->retimer_phys[i]) ++ continue; ++ ++ err = phy_set_mode_ext(mac->retimer_phys[i], PHY_MODE_ETHERNET, state->interface); ++ if (err) ++ netdev_err(mac->net_dev, "phy_set_mode_ext() on retimer = %d\n", err); ++ } + } + + static void dpaa2_mac_link_up(struct phylink_config *config, +@@ -345,23 +355,45 @@ static void dpaa2_mac_set_supported_interfaces(struct dpaa2_mac *mac) + + void dpaa2_mac_start(struct dpaa2_mac *mac) + { ++ int i; ++ + if (mac->serdes_phy) + phy_power_on(mac->serdes_phy); ++ ++ for (i = 0; i < sizeof(mac->retimer_phys)/sizeof(mac->retimer_phys[0]); i++) { ++ if (!mac->retimer_phys[i]) ++ continue; ++ ++ phy_power_on(mac->retimer_phys[i]); ++ } + } + + void dpaa2_mac_stop(struct dpaa2_mac *mac) + { ++ int i; ++ + if (mac->serdes_phy) + phy_power_off(mac->serdes_phy); ++ ++ for (i = 0; i < sizeof(mac->retimer_phys)/sizeof(mac->retimer_phys[0]); i++) { ++ if (!mac->retimer_phys[i]) ++ continue; ++ ++ phy_power_off(mac->retimer_phys[i]); ++ } + } + + int dpaa2_mac_connect(struct dpaa2_mac *mac) + { + struct net_device *net_dev = mac->net_dev; + struct fwnode_handle *dpmac_node; +- struct phy *serdes_phy = NULL; ++ struct phy *phy = NULL; + struct phylink *phylink; ++ const char *phy_name; + int err; ++ int phy_count; ++ int retimer_phy_count = 0; ++ int i; + + mac->if_link_type = mac->attr.link_type; + +@@ -376,19 +408,41 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + return -EINVAL; + mac->if_mode = err; + +- if (mac->features & DPAA2_MAC_FEATURE_PROTOCOL_CHANGE && +- !phy_interface_mode_is_rgmii(mac->if_mode) && +- is_of_node(dpmac_node)) { +- serdes_phy = of_phy_get(to_of_node(dpmac_node), NULL); +- +- if (serdes_phy == ERR_PTR(-ENODEV)) +- serdes_phy = NULL; +- else if (IS_ERR(serdes_phy)) +- return PTR_ERR(serdes_phy); +- else +- phy_init(serdes_phy); +- } +- mac->serdes_phy = serdes_phy; ++ /* parse serdes & retimer phys, if any */ ++ phy_count = of_count_phandle_with_args(to_of_node(dpmac_node), "phys", "#phy-cells"); ++ if (phy_count >= 0) { ++ for (i = 0; i < phy_count; i++) { ++ phy = of_phy_get_by_index(to_of_node(dpmac_node), i); ++ if (IS_ERR(phy)) ++ return PTR_ERR(phy); ++ ++ err = of_property_read_string_index(to_of_node(dpmac_node), "phy-names", i, &phy_name); ++ if (err || !strcmp("serdes", phy_name)) { ++ if (phy_interface_mode_is_rgmii(mac->if_mode)) { ++ netdev_err(net_dev, "rgmii ports don't support serdes phys\n"); ++ return -EINVAL; ++ } ++ ++ if (mac->serdes_phy) { ++ netdev_warn(net_dev, "unsupported number of serdes phys\n"); ++ continue; ++ } ++ ++ phy_init(phy); ++ mac->serdes_phy = phy; ++ } else if (!strcmp("retimer", phy_name)) { ++ if (retimer_phy_count >= sizeof(mac->retimer_phys)/sizeof(mac->retimer_phys[0])) { ++ netdev_warn(net_dev, "unsupported number of retimer phys\n"); ++ continue; ++ } ++ ++ phy_init(phy); ++ mac->retimer_phys[retimer_phy_count++] = phy; ++ } else { ++ netdev_warn(net_dev, "unsupported phy \"%s\"\n", phy_name); ++ } ++ } ++ } + + /* The MAC does not have the capability to add RGMII delays so + * error out if the interface mode requests them and there is no PHY +@@ -449,6 +503,8 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + + void dpaa2_mac_disconnect(struct dpaa2_mac *mac) + { ++ int i; ++ + if (!mac->phylink) + return; + +@@ -458,6 +514,10 @@ void dpaa2_mac_disconnect(struct dpaa2_mac *mac) + phylink_destroy(mac->phylink); + dpaa2_pcs_destroy(mac); + of_phy_put(mac->serdes_phy); ++ for (i = 0; i < sizeof(mac->retimer_phys)/sizeof(mac->retimer_phys[0]); i++) { ++ of_phy_put(mac->retimer_phys[i]); ++ mac->retimer_phys[i] = NULL; ++ } + mac->serdes_phy = NULL; + } + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +index cf94475c5e94..35ab362bdcf6 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +@@ -30,6 +30,7 @@ struct dpaa2_mac { + + int phy_req_state; + struct phy *serdes_phy; ++ struct phy *retimer_phys[2]; + }; + + bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, +-- +2.43.0 + diff --git a/patches/linux/0022-net-phy-create-driver-for-ds250df4x10-retimer.patch b/patches/linux/0022-net-phy-create-driver-for-ds250df4x10-retimer.patch new file mode 100644 index 0000000..5663e99 --- /dev/null +++ b/patches/linux/0022-net-phy-create-driver-for-ds250df4x10-retimer.patch @@ -0,0 +1,424 @@ +From 04b3f07ac09c89adaad35af3dd3025e9ae4b43f3 Mon Sep 17 00:00:00 2001 +From: Josua Mayer +Date: Wed, 12 Oct 2022 18:46:09 +0300 +Subject: [PATCH 22/23] 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 +--- + 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 ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#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, ®, 0xFF); ++ if (ret) ++ goto no_phys; ++ device_id = reg; ++ ++ /* read device version */ ++ ret = ds250dfx10_read_register(client, DS250DF410_REG_VERSION, ®, 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, ®, ++ 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, ®, 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 "); ++MODULE_DESCRIPTION("TI DS250DFX10 Retimer Driver"); ++MODULE_LICENSE("GPL"); +-- +2.43.0 + diff --git a/patches/linux/0023-arm64-dts-lx2162-clearfog-add-description-for-retime.patch b/patches/linux/0023-arm64-dts-lx2162-clearfog-add-description-for-retime.patch new file mode 100644 index 0000000..6c6d3f1 --- /dev/null +++ b/patches/linux/0023-arm64-dts-lx2162-clearfog-add-description-for-retime.patch @@ -0,0 +1,52 @@ +From 51928756a380284f232f1ca4d15c1d00ef7d84c7 Mon Sep 17 00:00:00 2001 +From: Josua Mayer +Date: Sun, 16 Apr 2023 13:24:31 +0300 +Subject: [PATCH 23/23] 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 +--- + .../boot/dts/freescale/fsl-lx2162a-clearfog.dts | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts +index eafef8718a0f..1758e91fdbcf 100644 +--- a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts ++++ b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts +@@ -103,13 +103,15 @@ &dpmac4 { + &dpmac5 { + sfp = <&sfp_bt>; + managed = "in-band-status"; +- phys = <&serdes_1 5>; ++ phys = <&serdes_1 5>, <&retimer 2>, <&retimer 3>; ++ phy-names = "serdes", "retimer", "retimer"; + }; + + &dpmac6 { + sfp = <&sfp_bb>; + managed = "in-band-status"; +- phys = <&serdes_1 4>; ++ phys = <&serdes_1 4>, <&retimer 0>, <&retimer 1>; ++ phy-names = "serdes", "retimer", "retimer"; + }; + + &dpmac11 { +@@ -241,7 +243,11 @@ ðernet_phy0 { + &i2c2 { + status = "okay"; + +- /* retimer@18 */ ++ retimer: retimer@18 { ++ compatible = "ti,ds250df410"; ++ reg = <0x18>; ++ #phy-cells = <1>; ++ }; + + i2c-mux@70 { + compatible = "nxp,pca9546"; +-- +2.43.0 +