| /* |
| * Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(mdio_adin2111, CONFIG_MDIO_LOG_LEVEL); |
| |
| #define DT_DRV_COMPAT adi_adin2111_mdio |
| |
| #include <stdint.h> |
| #include <errno.h> |
| #include <zephyr/device.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/drivers/mdio.h> |
| #include <zephyr/drivers/mdio/mdio_adin2111.h> |
| #include <zephyr/drivers/ethernet/eth_adin2111.h> |
| |
| /* 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) |