drivers: phy: add adin2111

Adds PHY driver. Works via MDIO API and
exposed ADIN2111 MDIO Clause 45
functions.

Link status detection is triggered by
ADIN2111 driver within offloaded IRQ
handler.

Supports:
  - LED0, LED1 enable/disable
  - Fatal HW error detection
  - AN 2.4V tx mode enable/disable

The initialization order is important.
PHY 2 must be initialized after PHY1.
Therefore, it shall be defined after the 1st one
in the devicetree.

Signed-off-by: Georgij Cernysiov <geo.cgv@gmail.com>
diff --git a/CODEOWNERS b/CODEOWNERS
index fad3a13..e5fc127 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -287,6 +287,7 @@
 /drivers/ethernet/*smsc91x*               @sgrrzhf
 /drivers/ethernet/*adin2111*              @GeorgeCGV
 /drivers/ethernet/phy/                    @rlubos @tbursztyka @arvinf
+/drivers/ethernet/phy/*adin2111*          @GeorgeCGV
 /drivers/mdio/                            @rlubos @tbursztyka @arvinf
 /drivers/mdio/*adin2111*                  @GeorgeCGV
 /drivers/flash/                           @nashif @de-nordic
diff --git a/drivers/ethernet/phy/CMakeLists.txt b/drivers/ethernet/phy/CMakeLists.txt
index 2592f9d..ce4cbe3 100644
--- a/drivers/ethernet/phy/CMakeLists.txt
+++ b/drivers/ethernet/phy/CMakeLists.txt
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: Apache-2.0
 
-zephyr_library_sources_ifdef(CONFIG_PHY_GENERIC_MII	 phy_mii.c)
+zephyr_library_sources_ifdef(CONFIG_PHY_GENERIC_MII	phy_mii.c)
+zephyr_library_sources_ifdef(CONFIG_PHY_ADIN2111	phy_adin2111.c)
diff --git a/drivers/ethernet/phy/Kconfig b/drivers/ethernet/phy/Kconfig
index 070bd89..bd5d9fb 100644
--- a/drivers/ethernet/phy/Kconfig
+++ b/drivers/ethernet/phy/Kconfig
@@ -23,12 +23,21 @@
 
 config PHY_GENERIC_MII
 	bool "Generic MII PHY Driver"
-	default y
+	default y if !ETH_ADIN2111
 	depends on MDIO
 	help
 	  This is a generic MII PHY interface that communicates with the
 	  PHY using the MDIO bus.
 
+config PHY_ADIN2111
+	bool "ADIN2111 PHY driver"
+	default y
+	depends on DT_HAS_ADI_ADIN2111_PHY_ENABLED
+	depends on ETH_ADIN2111
+	depends on MDIO_ADIN2111
+	help
+	  Enable ADIN2111 PHY driver.
+
 config PHY_AUTONEG_TIMEOUT_MS
 	int "Auto-negotiation timeout value in milliseconds"
 	default 4000
diff --git a/drivers/ethernet/phy/phy_adin2111.c b/drivers/ethernet/phy/phy_adin2111.c
new file mode 100644
index 0000000..843d2d7
--- /dev/null
+++ b/drivers/ethernet/phy/phy_adin2111.c
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(phy_adin2111, CONFIG_PHY_LOG_LEVEL);
+
+#define DT_DRV_COMPAT adi_adin2111_phy
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <zephyr/kernel.h>
+#include <zephyr/device.h>
+#include <zephyr/sys/util.h>
+#include <zephyr/net/phy.h>
+#include <zephyr/net/mii.h>
+#include <zephyr/drivers/mdio.h>
+#include <zephyr/drivers/mdio/mdio_adin2111.h>
+
+/* PHYs out of reset check retry delay */
+#define ADIN2111_PHY_AWAIT_DELAY_POLL_US			15U
+/* Number of retries for PHYs out of reset check */
+#define ADIN2111_PHY_AWAIT_RETRY_COUNT				200U
+
+/* PHY's software powerdown check retry delay */
+#define ADIN2111_PHY_SFT_PD_DELAY_POLL_US			15U
+/* Number of retries for PHY's software powerdown check */
+#define ADIN2111_PHY_SFT_PD_RETRY_COUNT				200U
+
+/* PHYs autonegotiation complete timeout */
+#define ADIN2111_AN_COMPLETE_AWAIT_TIMEOUT_MS			3000U
+
+/* ADIN2111 PHY identifier */
+#define ADIN2111_PHY_ID						0x0283BCA1U
+
+/* 10BASE-T1L PMA Status Register */
+#define ADIN2111_PHY_PMA_STATUS					0x000108F7U
+/* Indicates PHY support of 10BASE-T1L high voltage (2.4V) tx level op mode */
+#define ADIN2111_PHY_PMA_STATUS_B10L_TX_LVL_HI_ABLE		BIT(12)
+
+/* BASE-T1 Autonegotiation Control Register */
+#define ADIN2111_PHY_AN_CONTROL					0x00070200U
+/* Autonegotiation Enable */
+#define ADIN2111_PHY_AN_CONTROL_AN_EN				BIT(12)
+/* Autonegotiation Restart */
+#define ADIN2111_PHY_AN_CONTROL_AN_RESTART			BIT(9)
+
+/* BASE-T1 Autonegotiation Status Register */
+#define ADIN2111_PHY_AN_STATUS					0x00070201U
+/* Autonegotiation Complete */
+#define ADIN2111_PHY_AN_STATUS_AN_COMPLETE			BIT(5)
+/* Link Status */
+#define ADIN2111_PHY_AN_STATUS_AN_LINK_STATUS			BIT(2)
+
+/* 10BASE-T1 Autonegotiation Advertisement Register */
+#define ADIN2111_PHY_AN_ADV_ABILITY_H				0x00070204U
+/* Advertise PHY capability of 2.4V tx level op mode */
+#define ADIN2111_PHY_AN_ADV_ABILITY_H_B10L_TX_LVL_HI_ABL	BIT(13)
+/* Advertise PHY request of 2.4V tx level op mode */
+#define ADIN2111_PHY_AN_ADV_ABILITY_H_B10L_TX_LVL_HI_REQ	BIT(12)
+
+/* System Interrupt Mask Register */
+#define ADIN2111_PHY_CRSM_IRQ_MASK				0x001E0020U
+/* System Interrupt Status Register */
+#define ADIN2111_PHY_CRSM_IRQ_STATUS				0x001E0010U
+/**
+ * Mask of reserved interrupts that indicates a fatal error in the system.
+ *
+ * There is inconsistency between RM and ADI driver example:
+ *   - RM mask 0x6FFF
+ *   - ADI driver example mask 0x2BFF
+ *
+ * The value from the example doesn't include reserved bits 10 and 14.
+ * The tests show that PHY is still functioning when bit 10 is raised.
+ *
+ * Here the value from ADI driver example is used instead of RM.
+ */
+#define ADIN2111_PHY_CRSM_IRQ_STATUS_FATAL_ERR			0x2BFFU
+
+/* PHY Subsystem Interrupt Mask Register */
+#define ADIN2111_PHY_SUBSYS_IRQ_MASK				0x001F0021U
+/* PHY Subsystem Interrupt Status Register */
+#define ADIN2111_PHY_SUBSYS_IRQ_STATUS				0x001F0011U
+/* Link Status Change */
+#define ADIN2111_PHY_SUBSYS_IRQ_STATUS_LINK_STAT_CHNG_LH	BIT(1)
+
+/* Software Power-down Control Register */
+#define ADIN2111_PHY_CRSM_SFT_PD_CNTRL				0x001E8812U
+/* System Status Register */
+#define ADIN2111_PHY_CRSM_STAT					0x001E8818U
+/* Software Power-down Status */
+#define ADIN2111_CRSM_STAT_CRSM_SFT_PD_RDY			BIT(1)
+
+/* LED Control Register */
+#define ADIN2111_PHY_LED_CNTRL					0x001E8C82U
+/* LED 1 Enable */
+#define ADIN2111_PHY_LED_CNTRL_LED1_EN				BIT(15)
+/* LED 0 Enable */
+#define ADIN2111_PHY_LED_CNTRL_LED0_EN				BIT(7)
+
+struct phy_adin2111_config {
+	const struct device *mdio;
+	uint8_t phy_addr;
+	bool led0_en;
+	bool led1_en;
+	bool tx_24v;
+};
+
+struct phy_adin2111_data {
+	struct phy_link_state state;
+	struct k_sem sem;
+};
+
+static inline int phy_adin2111_c22_read(const struct device *dev, uint16_t reg,
+					uint16_t *val)
+{
+	const struct phy_adin2111_config *const cfg = dev->config;
+
+	return mdio_read(cfg->mdio, cfg->phy_addr, reg, val);
+}
+
+static inline int phy_adin2111_c22_write(const struct device *dev, uint16_t reg,
+					 uint16_t val)
+{
+	const struct phy_adin2111_config *const cfg = dev->config;
+
+	return mdio_write(cfg->mdio, cfg->phy_addr, reg, val);
+}
+
+static inline int phy_adin2111_c45_write(const struct device *dev, uint32_t reg,
+					 uint16_t val)
+{
+	const struct phy_adin2111_config *cfg = dev->config;
+
+	return adin2111_mdio_c45_write(cfg->mdio, cfg->phy_addr, ((reg >> 16U) & 0x1FU),
+				       (reg & UINT16_MAX), val);
+}
+
+static inline int phy_adin2111_c45_read(const struct device *dev, uint32_t reg,
+					uint16_t *val)
+{
+	const struct phy_adin2111_config *cfg = dev->config;
+
+	return adin2111_mdio_c45_read(cfg->mdio, cfg->phy_addr, ((reg >> 16U) & 0x1FU),
+				      (reg & 0xFFFFU), val);
+}
+
+static int phy_adin2111_reg_read(const struct device *dev, uint16_t reg_addr,
+				 uint32_t *data)
+{
+	const struct phy_adin2111_config *cfg = dev->config;
+	int ret;
+
+	mdio_bus_enable(cfg->mdio);
+
+	ret = phy_adin2111_c22_read(dev, reg_addr, (uint16_t *) data);
+
+	mdio_bus_disable(cfg->mdio);
+
+	return ret;
+}
+
+static int phy_adin2111_reg_write(const struct device *dev, uint16_t reg_addr,
+				  uint32_t data)
+{
+	const struct phy_adin2111_config *cfg = dev->config;
+	int ret;
+
+	mdio_bus_enable(cfg->mdio);
+
+	ret = phy_adin2111_c22_write(dev, reg_addr, (uint16_t) data);
+
+	mdio_bus_disable(cfg->mdio);
+
+	return ret;
+}
+
+static int phy_adin2111_await_phy(const struct device *dev)
+{
+	int ret;
+	uint32_t count;
+	uint16_t val;
+
+	/**
+	 * Port 2 PHY comes out of reset after Port 1 PHY,
+	 * wait until both are out of reset.
+	 * Reading Port 2 PHY registers returns 0s until
+	 * it comes out from reset.
+	 */
+	for (count = 0U; count < ADIN2111_PHY_AWAIT_RETRY_COUNT; ++count) {
+		ret = phy_adin2111_c45_read(dev, ADIN2111_PHY_CRSM_IRQ_MASK, &val);
+		if (ret >= 0) {
+			if (val != 0U) {
+				break;
+			}
+			ret = -ETIMEDOUT;
+		}
+		k_sleep(K_USEC(ADIN2111_PHY_AWAIT_DELAY_POLL_US));
+	}
+
+	return ret;
+}
+
+static int phy_adin2111_an_state_read(const struct device *dev)
+{
+	struct phy_adin2111_data *const data = dev->data;
+	uint16_t bmsr;
+	int ret;
+
+	/* read twice to get actual link status, latch low */
+	ret = phy_adin2111_c22_read(dev, MII_BMSR, &bmsr);
+	if (ret < 0) {
+		return ret;
+	}
+	ret = phy_adin2111_c22_read(dev, MII_BMSR, &bmsr);
+	if (ret < 0) {
+		return ret;
+	}
+
+	data->state.is_up = !!(bmsr & MII_BMSR_LINK_STATUS);
+
+	return 0;
+}
+
+int phy_adin2111_handle_phy_irq(const struct device *dev,
+				struct phy_link_state *state)
+{
+	struct phy_adin2111_data *const data = dev->data;
+	uint16_t subsys_status;
+	int ret;
+
+	ret = phy_adin2111_c45_read(dev, ADIN2111_PHY_SUBSYS_IRQ_STATUS,
+				     &subsys_status);
+	if (ret < 0) {
+		return ret;
+	}
+
+	if ((subsys_status & ADIN2111_PHY_SUBSYS_IRQ_STATUS_LINK_STAT_CHNG_LH) == 0U) {
+		/* nothing to process */
+		return -EAGAIN;
+	}
+
+	k_sem_take(&data->sem, K_FOREVER);
+
+	ret = phy_adin2111_an_state_read(dev);
+
+	memcpy(state, &data->state, sizeof(struct phy_link_state));
+
+	k_sem_give(&data->sem);
+
+	return ret;
+}
+
+static int phy_adin2111_sft_pd(const struct device *dev, bool enter)
+{
+	int ret;
+	uint32_t count;
+	const uint16_t expected = enter ? ADIN2111_CRSM_STAT_CRSM_SFT_PD_RDY : 0U;
+	uint16_t val;
+
+	ret = phy_adin2111_c45_write(dev, ADIN2111_PHY_CRSM_SFT_PD_CNTRL,
+				      enter ? 1U : 0U);
+	if (ret < 0) {
+		return ret;
+	}
+
+	for (count = 0U; count < ADIN2111_PHY_SFT_PD_RETRY_COUNT; ++count) {
+		ret = phy_adin2111_c45_read(dev, ADIN2111_PHY_CRSM_STAT,
+					     &val);
+		if (ret >= 0) {
+			if ((val & ADIN2111_CRSM_STAT_CRSM_SFT_PD_RDY) == expected) {
+				break;
+			}
+			ret = -ETIMEDOUT;
+		}
+		k_sleep(K_USEC(ADIN2111_PHY_SFT_PD_DELAY_POLL_US));
+	}
+
+	return ret;
+}
+
+static int phy_adin2111_id(const struct device *dev, uint32_t *phy_id)
+{
+	uint16_t val;
+
+	if (phy_adin2111_c22_read(dev, MII_PHYID1R, &val) < 0) {
+		return -EIO;
+	}
+
+	*phy_id = (val & UINT16_MAX) << 16;
+
+	if (phy_adin2111_c22_read(dev, MII_PHYID2R, &val) < 0) {
+		return -EIO;
+	}
+
+	*phy_id |= (val & UINT16_MAX);
+
+	return 0;
+}
+
+static int phy_adin2111_get_link_state(const struct device *dev,
+				       struct phy_link_state *state)
+{
+	struct phy_adin2111_data *const data = dev->data;
+
+	k_sem_take(&data->sem, K_FOREVER);
+
+	memcpy(state, &data->state, sizeof(struct phy_link_state));
+
+	k_sem_give(&data->sem);
+
+	return 0;
+}
+
+static int phy_adin2111_cfg_link(const struct device *dev,
+				 enum phy_link_speed adv_speeds)
+{
+	ARG_UNUSED(dev);
+
+	if (!!(adv_speeds & LINK_FULL_10BASE_T)) {
+		return 0;
+	}
+
+	return -ENOTSUP;
+}
+
+static int phy_adin2111_init(const struct device *dev)
+{
+	const struct phy_adin2111_config *const cfg = dev->config;
+	struct phy_adin2111_data *const data = dev->data;
+	uint32_t phy_id;
+	uint16_t val;
+	bool tx_24v_supported = false;
+	int ret;
+
+	data->state.is_up = false;
+	data->state.speed = LINK_FULL_10BASE_T;
+
+	ret = phy_adin2111_await_phy(dev);
+	if (ret < 0) {
+		LOG_ERR("PHY %u didn't come out of reset, %d",
+			cfg->phy_addr, ret);
+		return -ENODEV;
+	}
+
+	ret = phy_adin2111_id(dev, &phy_id);
+	if (ret < 0) {
+		LOG_ERR("Failed to read PHY %u ID, %d",
+			cfg->phy_addr, ret);
+		return -ENODEV;
+	}
+
+	if (phy_id != ADIN2111_PHY_ID) {
+		LOG_ERR("PHY %u unexpected PHY ID %X", cfg->phy_addr, phy_id);
+		return -EINVAL;
+	}
+
+	LOG_INF("PHY %u ID %X", cfg->phy_addr, phy_id);
+
+	/* enter software powerdown */
+	ret = phy_adin2111_sft_pd(dev, true);
+	if (ret < 0) {
+		return ret;
+	}
+
+	/* disable interrupts */
+	ret = phy_adin2111_c45_write(dev, ADIN2111_PHY_CRSM_IRQ_MASK, 0U);
+	if (ret < 0) {
+		return ret;
+	}
+
+	/* enable link status change irq */
+	ret = phy_adin2111_c45_write(dev, ADIN2111_PHY_SUBSYS_IRQ_MASK,
+				     ADIN2111_PHY_SUBSYS_IRQ_STATUS_LINK_STAT_CHNG_LH);
+	if (ret < 0) {
+		return ret;
+	}
+
+	/* clear PHY IRQ status before enabling ADIN IRQs */
+	ret = phy_adin2111_c45_read(dev, ADIN2111_PHY_CRSM_IRQ_STATUS, &val);
+	if (ret < 0) {
+		return ret;
+	}
+
+	if (val & ADIN2111_PHY_CRSM_IRQ_STATUS_FATAL_ERR) {
+		LOG_ERR("PHY %u CRSM reports fatal system error", cfg->phy_addr);
+		return -ENODEV;
+	}
+
+	ret = phy_adin2111_c45_read(dev, ADIN2111_PHY_SUBSYS_IRQ_STATUS, &val);
+	if (ret < 0) {
+		return ret;
+	}
+
+	if (!cfg->led0_en || !cfg->led1_en) {
+		ret = phy_adin2111_c45_read(dev, ADIN2111_PHY_LED_CNTRL, &val);
+		if (ret < 0) {
+			return ret;
+		}
+		if (!cfg->led0_en) {
+			val &= ~(ADIN2111_PHY_LED_CNTRL_LED0_EN);
+		}
+		if (!cfg->led1_en) {
+			val &= ~(ADIN2111_PHY_LED_CNTRL_LED1_EN);
+		}
+		ret = phy_adin2111_c45_write(dev, ADIN2111_PHY_LED_CNTRL, val);
+		if (ret < 0) {
+			return ret;
+		}
+	}
+
+	/* check 2.4V support */
+	ret = phy_adin2111_c45_read(dev, ADIN2111_PHY_PMA_STATUS, &val);
+	if (ret < 0) {
+		return ret;
+	}
+
+	tx_24v_supported = !!(val & ADIN2111_PHY_PMA_STATUS_B10L_TX_LVL_HI_ABLE);
+
+	LOG_INF("PHY %u 2.4V mode %s", cfg->phy_addr,
+		tx_24v_supported ? "supported" : "not supported");
+
+	if (!cfg->tx_24v & tx_24v_supported) {
+		LOG_ERR("PHY %u 2.4V mode supported, but not enabled",
+			cfg->phy_addr);
+	}
+
+	/* config 2.4V auto-negotiation */
+	ret = phy_adin2111_c45_read(dev, ADIN2111_PHY_AN_ADV_ABILITY_H,
+					&val);
+	if (ret < 0) {
+		return ret;
+	}
+
+	if (tx_24v_supported) {
+		val |= ADIN2111_PHY_AN_ADV_ABILITY_H_B10L_TX_LVL_HI_ABL;
+	} else {
+		val &= ~ADIN2111_PHY_AN_ADV_ABILITY_H_B10L_TX_LVL_HI_ABL;
+	}
+
+	if (cfg->tx_24v) {
+		if (!tx_24v_supported) {
+			LOG_ERR("PHY %u 2.4V mode enabled, but not supported",
+				cfg->phy_addr);
+			return -EINVAL;
+		}
+
+		val |= ADIN2111_PHY_AN_ADV_ABILITY_H_B10L_TX_LVL_HI_REQ;
+	} else {
+		val &= ~ADIN2111_PHY_AN_ADV_ABILITY_H_B10L_TX_LVL_HI_REQ;
+	}
+
+	ret = phy_adin2111_c45_write(dev, ADIN2111_PHY_AN_ADV_ABILITY_H,
+				     val);
+	if (ret < 0) {
+		return ret;
+	}
+
+	/* enable auto-negotiation */
+	ret = phy_adin2111_c45_write(dev, ADIN2111_PHY_AN_CONTROL,
+				     ADIN2111_PHY_AN_CONTROL_AN_EN);
+	if (ret < 0) {
+		return ret;
+	}
+
+	/**
+	 * done, PHY is in software powerdown (SFT PD)
+	 * exit software powerdown, PHY 1 has to exit before PHY 2
+	 * correct PHY order is expected to be in DTS to guarantee that
+	 */
+	return phy_adin2111_sft_pd(dev, false);
+}
+
+static int phy_adin2111_link_cb_set(const struct device *dev, phy_callback_t cb,
+				    void *user_data)
+{
+	ARG_UNUSED(dev);
+	ARG_UNUSED(cb);
+	ARG_UNUSED(user_data);
+	return -ENOTSUP;
+}
+
+static const struct ethphy_driver_api phy_adin2111_api = {
+	.get_link = phy_adin2111_get_link_state,
+	.cfg_link = phy_adin2111_cfg_link,
+	.link_cb_set = phy_adin2111_link_cb_set,
+	.read = phy_adin2111_reg_read,
+	.write = phy_adin2111_reg_write,
+};
+
+#define ADIN2111_PHY_INITIALIZE(n)						\
+	static const struct phy_adin2111_config phy_adin2111_config_##n = {	\
+		.mdio = DEVICE_DT_GET(DT_INST_BUS(n)),				\
+		.phy_addr = DT_INST_REG_ADDR(n),				\
+		.led0_en = DT_INST_PROP(n, led0_en),				\
+		.led1_en = DT_INST_PROP(n, led1_en),				\
+		.tx_24v = !(DT_INST_PROP(n, disable_tx_mode_24v)),		\
+	};									\
+	static struct phy_adin2111_data phy_adin2111_data_##n = {		\
+		.sem = Z_SEM_INITIALIZER(phy_adin2111_data_##n.sem, 1, 1),	\
+	};									\
+	DEVICE_DT_INST_DEFINE(n, &phy_adin2111_init, NULL,			\
+			      &phy_adin2111_data_##n, &phy_adin2111_config_##n, \
+			      POST_KERNEL, CONFIG_PHY_INIT_PRIORITY,		\
+			      &phy_adin2111_api);
+
+DT_INST_FOREACH_STATUS_OKAY(ADIN2111_PHY_INITIALIZE)
diff --git a/drivers/ethernet/phy/phy_adin2111_priv.h b/drivers/ethernet/phy/phy_adin2111_priv.h
new file mode 100644
index 0000000..dc0c2d0
--- /dev/null
+++ b/drivers/ethernet/phy/phy_adin2111_priv.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef PHY_ADIN2111_PRIV_H__
+#define PHY_ADIN2111_PRIV_H__
+
+#include <zephyr/device.h>
+#include <zephyr/net/phy.h>
+
+/**
+ * @brief Handles PHY interrupt.
+ *
+ * @note Used internally by the ADIN offloaded ISR handler.
+ *       The caller is responsible for device lock.
+ *       Shall not be called from ISR.
+ *
+ * @param[in] dev PHY device.
+ * @param[out] state Output of the link state.
+ *
+ * @retval 0 Successful and link state changed.
+ * @retval -EAGAIN Successful but link state didn't change.
+ * @retval <0 MDIO error.
+ */
+int phy_adin2111_handle_phy_irq(const struct device *dev,
+				struct phy_link_state *state);
+
+#endif /* PHY_ADIN2111_PRIV_H__ */
diff --git a/dts/bindings/ethernet/adi,adin2111-phy.yaml b/dts/bindings/ethernet/adi,adin2111-phy.yaml
new file mode 100644
index 0000000..ca7bb7e
--- /dev/null
+++ b/dts/bindings/ethernet/adi,adin2111-phy.yaml
@@ -0,0 +1,25 @@
+# Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH
+# SPDX-License-Identifier: Apache-2.0
+
+description: ADIN2111 PHY
+
+compatible: "adi,adin2111-phy"
+
+include: phy.yaml
+
+on-bus: mdio
+
+properties:
+  reg:
+    required: true
+    description: 5-bit physical/port address (PRTAD).
+  led0-en:
+    type: boolean
+    description: Enable LED 0.
+  led1-en:
+    type: boolean
+    description: Enable LED 1.
+  disable-tx-mode-24v:
+    type: boolean
+    description: |
+      Disables requirement of 2.4V TX operating mode in the AN advertisement.