From: Maxime Chevallier <maxime.chevallier@bootlin.com>
To: Artem Shimko <a.shimko.dev@gmail.com>,
netdev@vger.kernel.org, Andrew Lunn <andrew@lunn.ch>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>
Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org
Subject: Re: [PATCH net-next v2 2/2] net: phy: add DAPU Telecom DAP8210R(I) Gigabit Ethernet PHY driver
Date: Thu, 16 Jul 2026 15:01:57 +0200 [thread overview]
Message-ID: <c7193123-72e1-4bf5-85f7-eb68a4000c44@bootlin.com> (raw)
In-Reply-To: <20260716113805.593215-3-a.shimko.dev@gmail.com>
Hi,
On 7/16/26 13:38, Artem Shimko wrote:
> Add a new PHY driver for the DAPU Telecom DAP8211R(I) Gigabit
> Ethernet PHY, which is commonly used in enterprise and industrial
> networking applications.
>
> The driver implements extended register access via indirect addressing
> through corresponding registers, and provides comprehensive device tree
> support for RGMII delay configuration. The rx-internal-delay-ps and
> tx-internal-delay-ps properties allow precise tuning of clock delays in
> 150 ps steps from 0 to 2250 ps. Additionally, the optional
> dapu,tx-inverted-clk flag enables 180-degree TX clock phase shift for
> boards where signal integrity or MAC requirements necessitate clock
> inversion.
>
> Signed-off-by: Artem Shimko <a.shimko.dev@gmail.com>
> ---
> drivers/net/phy/Kconfig | 10 ++
> drivers/net/phy/Makefile | 1 +
> drivers/net/phy/dap8211r.c | 281 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 292 insertions(+)
> create mode 100644 drivers/net/phy/dap8211r.c
>
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> index 099f25dceabb..4576f707ac94 100644
> --- a/drivers/net/phy/Kconfig
> +++ b/drivers/net/phy/Kconfig
> @@ -237,6 +237,16 @@ config DAVICOM_PHY
> help
> Currently supports dm9161e and dm9131
>
> +config DAP8211R_PHY
> + tristate "DAPU Telecom DAP8211R(I) Gigabit Ethernet PHY"
> + depends on OF
> + help
> + Support for the DAPU Telecom DAP8211R(I) Gigabit Ethernet PHY.
> + This PHY is designed for enterprise and industrial networking
> + applications, supporting 10/100/1000 Mbps operation.
> + RGMII with: configurable TX/RX clock delays, optional flag to enable
> + 180-degree TX clock phase shift and internal packet generator.
> +
> config ICPLUS_PHY
> tristate "ICPlus PHYs"
> help
> diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
> index de660ae94945..ad35733eb4bb 100644
> --- a/drivers/net/phy/Makefile
> +++ b/drivers/net/phy/Makefile
> @@ -53,6 +53,7 @@ obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
> obj-$(CONFIG_CICADA_PHY) += cicada.o
> obj-$(CONFIG_CORTINA_PHY) += cortina.o
> obj-$(CONFIG_DAVICOM_PHY) += davicom.o
> +obj-$(CONFIG_DAP8211R_PHY) += dap8211r.o
> obj-$(CONFIG_DP83640_PHY) += dp83640.o
> obj-$(CONFIG_DP83822_PHY) += dp83822.o
> obj-$(CONFIG_DP83848_PHY) += dp83848.o
> diff --git a/drivers/net/phy/dap8211r.c b/drivers/net/phy/dap8211r.c
> new file mode 100644
> index 000000000000..e1e6a322ef0c
> --- /dev/null
> +++ b/drivers/net/phy/dap8211r.c
> @@ -0,0 +1,281 @@
> +// SPDX-License-Identifier: GPL
> +/*
> + * Driver for the DAPU Telecom DAP8211R(I) Gigabit Ethernet PHY.
> + *
> + * Specifications:
> + * - IEEE 802.3 10BASE-Te, 100BASE-TX, 1000BASE-T
> + * - IEEE 802.3az-2010 Energy Efficient Ethernet
> + * - IEEE 1588 SyncE support
> + * - RGMII
> + *
> + * Author: Artem Shimko <a.shimko.dev@gmail.com>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/errno.h>
> +#include <linux/ethtool.h>
> +#include <linux/kernel.h>
> +#include <linux/mii.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/of.h>
> +#include <linux/phy.h>
> +
> +#define DAP8211R_PHY_ID 0x0008011B
> +#define DAP8211R_PHY_ID_MASK 0xFFFFFFFF
> +
> +#define DAP8211R_EXT_ADD 0x1E
> +#define DAP8211R_EXT_DATA 0x1F
> +
> +#define DAP8211R_PHY_CON 0xA001
> +#define DAP8211R_PHY_SW_RST BIT(15)
> +
> +#define DAP8211R_RGMII_CON 0xA003
> +#define DAP8211R_RGMII_TX_DEL_MASK GENMASK(3, 0)
> +#define DAP8211R_RGMII_RX_DEL_MASK GENMASK(13, 10)
> +#define DAP8211R_RGMII_CLK_INVERT BIT(14)
> +
> +/* Default RGMII delay: 13 * 150 == 1.95ns */
> +#define DAP8211R_DEFAULT_DELAY_SEL 0xD
> +
> +struct dap8211r_delay_config {
> + u32 ps;
> + u8 sel;
> +};
> +
> +static const struct dap8211r_delay_config delay_config[] = {
> + { 0, 0},
> + { 150, 1},
> + { 300, 2},
> + { 450, 3},
> + { 600, 4},
> + { 750, 5},
> + { 900, 6},
> + {1050, 7},
> + {1200, 8},
> + {1350, 9},
> + {1500, 10},
> + {1650, 11},
> + {1800, 12},
> + {1950, 13},
> + {2100, 14},
> + {2250, 15},
> +};
> +
> +#define DAP8211R_DELAY_COUNT ARRAY_SIZE(delay_config)
> +
> +/**
> + * dap8211r_delay_ps_to_sel() - Convert ps to register value (exact match only)
> + * @ps: Delay in picoseconds
> + *
> + * Converts a delay value in picoseconds to the corresponding register value
> + * for RGMII delay configuration. The PHY supports specific values from
> + * 0 to 2250 ps in 150 ps steps.
> + *
> + * Return: Register value (0-15) on success, -EINVAL if @ps is not supported.
> + */
> +
> +static int dap8211r_delay_ps_to_sel(u32 ps)
> +{
> + for (int i = 0; i < DAP8211R_DELAY_COUNT; i++)
> + if (ps == delay_config[i].ps)
> + return delay_config[i].sel;
> +
> + return -EINVAL;
> +}
> +
> +/**
> + * dap8211r_read_ext() - Read extended register
> + * @phydev: PHY device structure
> + * @reg: Extended register address
> + *
> + * Reads a PHY extended register using the indirect access method.
> + * The caller must hold the MDIO bus lock.
> + *
> + * Return: Register value on success, or negative error code
> + */
> +static int dap8211r_read_ext(struct phy_device *phydev, u16 reg)
> +{
> + int ret;
> +
> + phy_lock_mdio_bus(phydev);
> + ret = __phy_write(phydev, DAP8211R_EXT_ADD, reg);
> + if (ret < 0)
> + goto out;
> +
> + ret = __phy_read(phydev, DAP8211R_EXT_DATA);
> +out:
> + phy_unlock_mdio_bus(phydev);
> + return ret;
> +}
> +
> +/**
> + * dap8211r_modify_ext() - Modify extended register bits
> + * @phydev: PHY device structure
> + * @reg: Extended register address
> + * @mask: Bit mask of bits to clear
> + * @set: Bit mask of bits to set
> + *
> + * Modifies a PHY extended register using the indirect access method.
> + * New value = (old value & ~mask) | set.
> + * The caller must hold the MDIO bus lock.
> + *
> + * Return: 0 on success, or negative error code
> + */
> +static int dap8211r_modify_ext(struct phy_device *phydev, u16 reg, u16 mask, u16 set)
> +{
> + int ret;
> +
> + phy_lock_mdio_bus(phydev);
> + ret = __phy_write(phydev, DAP8211R_EXT_ADD, reg);
> + if (ret < 0)
> + goto out;
> +
> + ret = __phy_modify(phydev, DAP8211R_EXT_DATA, mask, set);
> +out:
> + phy_unlock_mdio_bus(phydev);
> + return ret;
> +}
> +
> +/**
> + * dap8211r_get_rgmii_delay() - Get RGMII delay from DT
> + * @phydev: PHY device
> + * @prop_name: DT property name
> + * @is_id: If phy mode is PHY_INTERFACE_MODE_RGMII_[TXID,RXID,ID]
> + *
> + * Reads the RGMII delay from the device tree. If the property is not
> + * specified, the default delay (1950ps) is used.
> + *
> + * Return: Register value (0-15) on success, negative error code on failure.
> + * -EINVAL: Property not specified and is_id is false.
> + */
> +static int dap8211r_get_rgmii_delay(struct phy_device *phydev, const char *prop_name, bool is_id)
> +{
> + struct device_node *np = phydev->mdio.dev.of_node;
> + u32 ps = 0;
> + int ret;
> +
> + ret = of_property_read_u32(np, prop_name, &ps);
> + if (ret == -EINVAL)
> + return (is_id) ? DAP8211R_DEFAULT_DELAY_SEL : ret;
> + if (ret < 0)
> + return ret;
> +
> + return dap8211r_delay_ps_to_sel(ps);
> +}
> +
> +/**
> + * dap8211r_config_init() - Initialize PHY
> + * @phydev: PHY device structure
> + *
> + * Configures the PHY during initialization:
> + * - RGMII delays based on interface mode
> + * - TX clock invertion
> + * - Software reset to apply settings (low active, self clear)
> + *
> + * Return: 0 on success, or negative error code
> + */
> +static int dap8211r_config_init(struct phy_device *phydev)
> +{
> + struct device_node *phydev_node = phydev->mdio.dev.of_node;
> + u16 mask = 0, set = 0;
> + int ret, retries = 10;
> +
> + switch (phydev->interface) {
> + case PHY_INTERFACE_MODE_RGMII:
> + ret = dap8211r_get_rgmii_delay(phydev, "rx-internal-delay-ps", false);
> + if (ret >= 0) {
> + set = FIELD_PREP(DAP8211R_RGMII_RX_DEL_MASK, ret);
> + mask = DAP8211R_RGMII_RX_DEL_MASK;
> + } else if ((ret < 0) && (ret != -EINVAL)) {
> + return ret;
> + }
> +
> + ret = dap8211r_get_rgmii_delay(phydev, "tx-internal-delay-ps", false);
> + if (ret >= 0) {
> + set |= FIELD_PREP(DAP8211R_RGMII_TX_DEL_MASK, ret);
> + mask |= DAP8211R_RGMII_TX_DEL_MASK;
> + } else if ((ret < 0) && (ret != -EINVAL)) {
> + return ret;
> + }
> + break;
> + case PHY_INTERFACE_MODE_RGMII_RXID:
> + ret = dap8211r_get_rgmii_delay(phydev, "rx-internal-delay-ps", true);
> + if (ret < 0)
> + return ret;
> +
> + set = FIELD_PREP(DAP8211R_RGMII_RX_DEL_MASK, ret);
> + mask = DAP8211R_RGMII_RX_DEL_MASK;
> + break;
> + case PHY_INTERFACE_MODE_RGMII_ID:
> + ret = dap8211r_get_rgmii_delay(phydev, "rx-internal-delay-ps", true);
> + if (ret < 0)
> + return ret;
> +
> + set = FIELD_PREP(DAP8211R_RGMII_RX_DEL_MASK, ret);
> + mask = DAP8211R_RGMII_RX_DEL_MASK;
> + fallthrough;
> + case PHY_INTERFACE_MODE_RGMII_TXID:
> + ret = dap8211r_get_rgmii_delay(phydev, "tx-internal-delay-ps", true);
> + if (ret < 0)
> + return ret;
> +
> + set |= FIELD_PREP(DAP8211R_RGMII_TX_DEL_MASK, ret);
> + mask |= DAP8211R_RGMII_TX_DEL_MASK;
> + break;
> + default:
> + phydev_err(phydev, "Unsupported interface: %d\n",
> + phydev->interface);
> + return -EINVAL;
> + }
You can simplify the whole delay parsing a log by using phy_get_internal_delay().
It will give you the index of the delay from the delay table you have :)
https://elixir.bootlin.com/linux/v7.1.3/source/drivers/net/phy/phy_device.c#L3085
You can take a look at the few drivers that use it (mscc, dp83869) for reference
Maxime
next prev parent reply other threads:[~2026-07-16 13:02 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-16 11:38 [PATCH net-next v2 0/2] Add DAPU Telecom DAP8211R(I) Gigabit Ethernet PHY driver Artem Shimko
2026-07-16 11:38 ` [PATCH net-next v2 1/2] dt-bindings: net: add DAPU Telecom DAP8211R(I) PHY binding Artem Shimko
2026-07-16 11:38 ` [PATCH net-next v2 2/2] net: phy: add DAPU Telecom DAP8210R(I) Gigabit Ethernet PHY driver Artem Shimko
2026-07-16 13:01 ` Maxime Chevallier [this message]
2026-07-16 13:55 ` Andrew Lunn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=c7193123-72e1-4bf5-85f7-eb68a4000c44@bootlin.com \
--to=maxime.chevallier@bootlin.com \
--cc=a.shimko.dev@gmail.com \
--cc=andrew@lunn.ch \
--cc=conor+dt@kernel.org \
--cc=davem@davemloft.net \
--cc=devicetree@vger.kernel.org \
--cc=edumazet@google.com \
--cc=hkallweit1@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox