blob: 74e63c76d0654a981996dbd75e022cdb954464a1 [file] [log] [blame]
/*
* 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)