| /* |
| * Copyright (c) 2025 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT intel_igc_mdio |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/net/mdio.h> |
| #include <zephyr/drivers/mdio.h> |
| #include <zephyr/drivers/pcie/pcie.h> |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(intel_igc_mdio, CONFIG_MDIO_LOG_LEVEL); |
| |
| #define INTEL_IGC_MDIC_OFFSET 0x00020 |
| #define INTEL_IGC_MDIC_DATA_MASK GENMASK(15, 0) |
| #define INTEL_IGC_MDIC_REG_MASK GENMASK(20, 16) |
| #define INTEL_IGC_MDIC_PHY_MASK GENMASK(25, 21) |
| #define INTEL_IGC_MDIC_OP_MASK GENMASK(27, 26) |
| #define INTEL_IGC_MDIC_READY BIT(28) |
| #define INTEL_IGC_MMDCTRL 0xD |
| #define INTEL_IGC_MMDCTRL_ACTYPE_MASK GENMASK(15, 14) |
| #define INTEL_IGC_MMDCTRL_DEVAD_MASK GENMASK(4, 0) |
| #define INTEL_IGC_MMDDATA 0xE |
| #define INTEL_IGC_DEFAULT_DEVNUM 0 |
| |
| struct intel_igc_mdio_cfg { |
| const struct device *const platform; |
| }; |
| |
| struct intel_igc_mdio_data { |
| struct k_mutex mutex; |
| }; |
| |
| static int intel_igc_mdio(const struct device *dev, uint32_t command) |
| { |
| const struct intel_igc_mdio_cfg *cfg = dev->config; |
| struct intel_igc_mdio_data *data = dev->data; |
| mm_reg_t mdic; |
| int ret; |
| |
| mdic = DEVICE_MMIO_GET(cfg->platform) + INTEL_IGC_MDIC_OFFSET; |
| |
| k_mutex_lock(&data->mutex, K_FOREVER); |
| sys_write32(command, mdic); |
| /* Wait for the read or write transaction to complete */ |
| if (!WAIT_FOR((sys_read32(mdic) & INTEL_IGC_MDIC_READY), |
| CONFIG_MDIO_INTEL_BUSY_CHECK_TIMEOUT, k_usleep(1))) { |
| LOG_ERR("MDIC operation timed out"); |
| k_mutex_unlock(&data->mutex); |
| return -ETIMEDOUT; |
| } |
| ret = sys_read32(mdic); |
| k_mutex_unlock(&data->mutex); |
| |
| return ret; |
| } |
| |
| static int intel_igc_mdio_read(const struct device *dev, uint8_t prtad, uint8_t regad, |
| uint16_t *user_data) |
| { |
| int ret = 0; |
| uint32_t command = FIELD_PREP(INTEL_IGC_MDIC_PHY_MASK, prtad) | |
| FIELD_PREP(INTEL_IGC_MDIC_REG_MASK, regad) | |
| FIELD_PREP(INTEL_IGC_MDIC_OP_MASK, MDIO_OP_C22_READ); |
| |
| ret = intel_igc_mdio(dev, command); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| *user_data = FIELD_GET(INTEL_IGC_MDIC_DATA_MASK, ret); |
| |
| return 0; |
| } |
| |
| static int intel_igc_mdio_write(const struct device *dev, uint8_t prtad, uint8_t regad, |
| uint16_t user_data) |
| { |
| int ret; |
| |
| uint32_t command = FIELD_PREP(INTEL_IGC_MDIC_PHY_MASK, prtad) | |
| FIELD_PREP(INTEL_IGC_MDIC_REG_MASK, regad) | |
| FIELD_PREP(INTEL_IGC_MDIC_OP_MASK, MDIO_OP_C22_WRITE) | |
| FIELD_PREP(INTEL_IGC_MDIC_DATA_MASK, user_data); |
| |
| ret = intel_igc_mdio(dev, command); |
| |
| return ret < 0 ? ret : 0; |
| } |
| |
| static int intel_igc_mdio_pre_handle_c45(const struct device *dev, uint8_t prtad, uint8_t devnum, |
| uint16_t regad) |
| { |
| int ret; |
| |
| /* Set device number using MMDCTRL */ |
| ret = intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDCTRL, |
| (uint16_t)(FIELD_PREP(INTEL_IGC_MMDCTRL_DEVAD_MASK, devnum))); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* Set register address using MMDDATA */ |
| ret = intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDDATA, regad); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* Set device number and access type as data using MMDCTRL */ |
| return intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDCTRL, |
| (uint16_t)(FIELD_PREP(INTEL_IGC_MMDCTRL_ACTYPE_MASK, 1) | |
| FIELD_PREP(INTEL_IGC_MMDCTRL_DEVAD_MASK, devnum))); |
| } |
| |
| static int intel_igc_mdio_post_handle_c45(const struct device *dev, uint8_t prtad) |
| { |
| /* Restore default device number using MMDCTRL */ |
| return intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDCTRL, INTEL_IGC_DEFAULT_DEVNUM); |
| } |
| |
| static int intel_igc_mdio_read_c45(const struct device *dev, uint8_t prtad, uint8_t devnum, |
| uint16_t regad, uint16_t *user_data) |
| { |
| int ret = intel_igc_mdio_pre_handle_c45(dev, prtad, devnum, regad); |
| |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* Read user data using MMDDATA */ |
| ret = intel_igc_mdio_read(dev, prtad, INTEL_IGC_MMDDATA, user_data); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return intel_igc_mdio_post_handle_c45(dev, prtad); |
| } |
| |
| static int intel_igc_mdio_write_c45(const struct device *dev, uint8_t prtad, uint8_t devnum, |
| uint16_t regad, uint16_t user_data) |
| { |
| int ret = intel_igc_mdio_pre_handle_c45(dev, prtad, devnum, regad); |
| |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* Write the user_data using MMDDATA */ |
| ret = intel_igc_mdio_write(dev, prtad, INTEL_IGC_MMDDATA, user_data); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return intel_igc_mdio_post_handle_c45(dev, prtad); |
| } |
| |
| static DEVICE_API(mdio, mdio_api) = { |
| .read = intel_igc_mdio_read, |
| .write = intel_igc_mdio_write, |
| .read_c45 = intel_igc_mdio_read_c45, |
| .write_c45 = intel_igc_mdio_write_c45, |
| }; |
| |
| #define INTEL_IGC_MDIO_INIT(n) \ |
| static struct intel_igc_mdio_data mdio_data_##n = { \ |
| .mutex = Z_MUTEX_INITIALIZER(mdio_data_##n.mutex), \ |
| }; \ |
| static struct intel_igc_mdio_cfg mdio_cfg_##n = { \ |
| .platform = DEVICE_DT_GET(DT_INST_PARENT(n)), \ |
| }; \ |
| DEVICE_DT_INST_DEFINE(n, NULL, NULL, &mdio_data_##n, &mdio_cfg_##n, POST_KERNEL, \ |
| CONFIG_MDIO_INIT_PRIORITY, &mdio_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(INTEL_IGC_MDIO_INIT) |