diff --git a/CODEOWNERS b/CODEOWNERS index ae0f1b4207..fad3a13ad7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -288,6 +288,7 @@ /drivers/ethernet/*adin2111* @GeorgeCGV /drivers/ethernet/phy/ @rlubos @tbursztyka @arvinf /drivers/mdio/ @rlubos @tbursztyka @arvinf +/drivers/mdio/*adin2111* @GeorgeCGV /drivers/flash/ @nashif @de-nordic /drivers/flash/*stm32_qspi* @lmajewski /drivers/flash/*b91* @andy-liu-telink diff --git a/drivers/mdio/CMakeLists.txt b/drivers/mdio/CMakeLists.txt index bb939287e3..7fc03a8fc8 100644 --- a/drivers/mdio/CMakeLists.txt +++ b/drivers/mdio/CMakeLists.txt @@ -6,3 +6,4 @@ zephyr_library_sources_ifdef(CONFIG_MDIO_SHELL mdio_shell.c) zephyr_library_sources_ifdef(CONFIG_MDIO_ATMEL_SAM mdio_sam.c) zephyr_library_sources_ifdef(CONFIG_MDIO_ESP32 mdio_esp32.c) zephyr_library_sources_ifdef(CONFIG_MDIO_NXP_S32_NETC mdio_nxp_s32_netc.c) +zephyr_library_sources_ifdef(CONFIG_MDIO_ADIN2111 mdio_adin2111.c) diff --git a/drivers/mdio/Kconfig b/drivers/mdio/Kconfig index 4fa8b9dd08..0f2d3b84a8 100644 --- a/drivers/mdio/Kconfig +++ b/drivers/mdio/Kconfig @@ -28,6 +28,7 @@ config MDIO_SHELL source "drivers/mdio/Kconfig.esp32" source "drivers/mdio/Kconfig.sam" source "drivers/mdio/Kconfig.nxp_s32" +source "drivers/mdio/Kconfig.adin2111" config MDIO_INIT_PRIORITY int "Init priority" diff --git a/drivers/mdio/Kconfig.adin2111 b/drivers/mdio/Kconfig.adin2111 new file mode 100644 index 0000000000..778bbbc254 --- /dev/null +++ b/drivers/mdio/Kconfig.adin2111 @@ -0,0 +1,10 @@ +# Copyright 2023 PHOENIX CONTACT Electronics GmbH +# SPDX-License-Identifier: Apache-2.0 + +config MDIO_ADIN2111 + bool "NXP S32 NETC External MDIO driver" + default y + depends on DT_HAS_ADI_ADIN2111_MDIO_ENABLED + depends on ETH_ADIN2111 + help + Enable ADIN2111 MDIO driver. diff --git a/drivers/mdio/mdio_adin2111.c b/drivers/mdio/mdio_adin2111.c new file mode 100644 index 0000000000..39d994c9cc --- /dev/null +++ b/drivers/mdio/mdio_adin2111.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(mdio_adin2111, CONFIG_MDIO_LOG_LEVEL); + +#define DT_DRV_COMPAT adi_adin2111_mdio + +#include +#include +#include +#include +#include +#include +#include + +/* MDIO ready check retry delay */ +#define ADIN2111_MDIO_READY_AWAIT_DELAY_POLL_US 5U +/* Number of retries for MDIO ready check */ +#define ADIN2111_MDIO_READY_AWAIT_RETRY_COUNT 10U + +/* MDIO Access Register 1 */ +#define ADIN2111_MDIOACC0 0x20U +/* MDIO Access Register 2 */ +#define ADIN2111_MDIOACC1 0x21U + +/* MDIO MDIOACC Transaction Done */ +#define ADIN211_MDIOACC_MDIO_TRDONE BIT(31) + +struct mdio_adin2111_config { + const struct device *adin; +}; + +static int mdio_adin2111_wait_ready(const struct device *dev, uint16_t reg, + uint32_t *out) +{ + const struct mdio_adin2111_config *const cfg = dev->config; + uint32_t count; + int ret; + + for (count = 0U; count < ADIN2111_MDIO_READY_AWAIT_RETRY_COUNT; ++count) { + ret = eth_adin2111_reg_read(cfg->adin, reg, out); + if (ret >= 0) { + if ((*out) & ADIN211_MDIOACC_MDIO_TRDONE) { + break; + } + ret = -ETIMEDOUT; + } + k_sleep(K_USEC(ADIN2111_MDIO_READY_AWAIT_DELAY_POLL_US)); + } + + return ret; +} + + +int adin2111_mdio_c45_read(const struct device *dev, uint8_t prtad, + uint8_t devad, uint16_t regad, + uint16_t *data) +{ + const struct mdio_adin2111_config *const cfg = dev->config; + uint32_t rdy; + uint32_t cmd; + int ret; + + /* address op */ + cmd = (prtad & 0x1FU) << 21; + cmd |= (devad & 0x1FU) << 16; + cmd |= regad; + + ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd); + if (ret < 0) { + return ret; + } + + /* read op */ + cmd = (cmd & ~UINT16_MAX) | (0x3U << 26); + + ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC1, cmd); + if (ret < 0) { + return ret; + } + + ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC1, &rdy); + if (ret < 0) { + return ret; + } + + /* read out */ + ret = eth_adin2111_reg_read(cfg->adin, ADIN2111_MDIOACC1, &cmd); + + *data = cmd & UINT16_MAX; + + return ret; +} + +int adin2111_mdio_c45_write(const struct device *dev, uint8_t prtad, + uint8_t devad, uint16_t regad, + uint16_t data) +{ + const struct mdio_adin2111_config *const cfg = dev->config; + + uint32_t rdy; + uint32_t cmd; + int ret; + + /* address op */ + cmd = (prtad & 0x1FU) << 21; + cmd |= (devad & 0x1FU) << 16; + cmd |= regad; + + ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd); + if (ret < 0) { + return ret; + } + + /* write op */ + cmd |= BIT(26); + cmd = (cmd & ~UINT16_MAX) | data; + + ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC1, cmd); + if (ret < 0) { + return ret; + } + + ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC1, &rdy); + + return ret; +} + +static int mdio_adin2111_read(const struct device *dev, uint8_t prtad, + uint8_t devad, uint16_t *data) +{ + const struct mdio_adin2111_config *const cfg = dev->config; + uint32_t read; + uint32_t cmd; + int ret; + + cmd = BIT(28); + cmd |= 0x3U << 26; + cmd |= (prtad & 0x1FU) << 21; + cmd |= (devad & 0x1FU) << 16; + + ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd); + if (ret >= 0) { + ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC0, &read); + *data = read & UINT16_MAX; + } + + return ret; +} + +static int mdio_adin2111_write(const struct device *dev, uint8_t prtad, + uint8_t devad, uint16_t data) +{ + const struct mdio_adin2111_config *const cfg = dev->config; + uint32_t cmd; + uint32_t rdy; + int ret; + + cmd = BIT(28); + cmd |= BIT(26); + cmd |= (prtad & 0x1FU) << 21; + cmd |= (devad & 0x1FU) << 16; + cmd |= data; + + ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd); + if (ret >= 0) { + ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC0, &rdy); + } + + return ret; +} + +static void mdio_adin2111_bus_enable(const struct device *dev) +{ + const struct mdio_adin2111_config *const cfg = dev->config; + + eth_adin2111_lock(cfg->adin, K_FOREVER); +} + +static void mdio_adin2111_bus_disable(const struct device *dev) +{ + const struct mdio_adin2111_config *const cfg = dev->config; + + eth_adin2111_unlock(cfg->adin); +} + +static const struct mdio_driver_api mdio_adin2111_api = { + .read = mdio_adin2111_read, + .write = mdio_adin2111_write, + .bus_enable = mdio_adin2111_bus_enable, + .bus_disable = mdio_adin2111_bus_disable +}; + +#define ADIN2111_MDIO_INIT(n) \ + static const struct mdio_adin2111_config mdio_adin2111_config_##n = { \ + .adin = DEVICE_DT_GET(DT_INST_BUS(n)), \ + }; \ + DEVICE_DT_INST_DEFINE(n, NULL, NULL, \ + NULL, &mdio_adin2111_config_##n, \ + POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, \ + &mdio_adin2111_api); + +DT_INST_FOREACH_STATUS_OKAY(ADIN2111_MDIO_INIT) diff --git a/dts/bindings/mdio/adi,adin2111-mdio.yaml b/dts/bindings/mdio/adi,adin2111-mdio.yaml new file mode 100644 index 0000000000..ab20d5cb33 --- /dev/null +++ b/dts/bindings/mdio/adi,adin2111-mdio.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH +# SPDX-License-Identifier: Apache-2.0 + +description: ADIN2111 MDIO Driver node + +compatible: "adi,adin2111-mdio" + +include: mdio-controller.yaml + +on-bus: adin2111 + +properties: + "#address-cells": + required: true + const: 1 + + "#size-cells": + required: true + const: 0 diff --git a/include/zephyr/drivers/mdio/mdio_adin2111.h b/include/zephyr/drivers/mdio/mdio_adin2111.h new file mode 100644 index 0000000000..bf97186143 --- /dev/null +++ b/include/zephyr/drivers/mdio/mdio_adin2111.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MDIO_ADIN2111_H__ +#define ZEPHYR_INCLUDE_DRIVERS_MDIO_ADIN2111_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Read from MDIO Bus using Clause 45 access + * + * @note The caller is responsible for device lock. + * Shall not be called from ISR. + * + * @param[in] dev MDIO device. + * @param[in] prtad Port address. + * @param[in] devad Device address. + * @param[in] regad Register address. + * @param[out] data Pointer to receive read data. + * + * @retval 0 If successful. + * @retval -EIO General input / output error. + * @retval -ETIMEDOUT If transaction timedout on the bus. + * @retval <0 Error, a negative errno code. + */ +int adin2111_mdio_c45_read(const struct device *dev, uint8_t prtad, + uint8_t devad, uint16_t regad, uint16_t *data); + +/** + * @brief Write to MDIO bus using Clause 45 access + * + * @note The caller is responsible for device lock. + * Shall not be called from ISR. + * + * @param[in] dev MDIO device. + * @param[in] prtad Port address. + * @param[in] devad Device address. + * @param[in] regad Register address. + * @param[in] data Data to write. + * + * @retval 0 If successful. + * @retval -EIO General input / output error. + * @retval -ETIMEDOUT If transaction timedout on the bus. + * @retval <0 Error, a negative errno code. + */ +int adin2111_mdio_c45_write(const struct device *dev, uint8_t prtad, + uint8_t devad, uint16_t regad, uint16_t data); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MDIO_ADIN2111_H__ */