drivers: mdio: Add NXP ENET QOS MDIO Driver

Add driver for NXP ENET QOS MDIO

Signed-off-by: Declan Snyder <declan.snyder@nxp.com>
This commit is contained in:
Declan Snyder 2024-03-01 14:33:15 -06:00 committed by Anas Nashif
parent ecfc38ff6f
commit e90fa0399b
5 changed files with 252 additions and 0 deletions

View file

@ -11,3 +11,4 @@ zephyr_library_sources_ifdef(CONFIG_MDIO_ADIN2111 mdio_adin2111.c)
zephyr_library_sources_ifdef(CONFIG_MDIO_GPIO mdio_gpio.c)
zephyr_library_sources_ifdef(CONFIG_MDIO_NXP_ENET mdio_nxp_enet.c)
zephyr_library_sources_ifdef(CONFIG_MDIO_INFINEON_XMC4XXX mdio_xmc4xxx.c)
zephyr_library_sources_ifdef(CONFIG_MDIO_NXP_ENET_QOS mdio_nxp_enet_qos.c)

View file

@ -32,6 +32,7 @@ source "drivers/mdio/Kconfig.adin2111"
source "drivers/mdio/Kconfig.gpio"
source "drivers/mdio/Kconfig.nxp_enet"
source "drivers/mdio/Kconfig.xmc4xxx"
source "drivers/mdio/Kconfig.nxp_enet_qos"
config MDIO_INIT_PRIORITY
int "Init priority"

View file

@ -0,0 +1,31 @@
# Copyright 2023 NXP
# SPDX-License-Identifier: Apache-2.0
config MDIO_NXP_ENET_QOS
bool "NXP ENET QoS MDIO driver"
default y
depends on DT_HAS_NXP_ENET_QOS_MDIO_ENABLED
help
Enable NXP ENET QOS (Quality of Service) MDIO driver.
if MDIO_NXP_ENET_QOS
config MDIO_NXP_ENET_QOS_RECHECK_COUNT
int "Number of times to recheck MDIO transaction status"
default 3
help
Number of times that the driver should recheck the status
of an MDIO bus transaction before timing out
Timeout time is:
CONFIG_MDIO_NXP_ENET_QOS_RECHECK_TIME * CONFIG_MDIO_NXP_ENET_QOS_RECHECK_COUNT
config MDIO_NXP_ENET_QOS_RECHECK_TIME
int "Time between rechecks of transaction status (us)"
default 100
help
The amount of time in microseconds that the driver should
busy wait between checks of the MDIO transaction status.
Timeout time is:
CONFIG_MDIO_NXP_ENET_QOS_RECHECK_TIME * CONFIG_MDIO_NXP_ENET_QOS_RECHECK_COUNT
endif

View file

@ -0,0 +1,217 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_enet_qos_mdio
#include <zephyr/net/mdio.h>
#include <zephyr/drivers/mdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/ethernet/eth_nxp_enet_qos.h>
#include <zephyr/sys/util.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(mdio_nxp_enet_qos, CONFIG_MDIO_LOG_LEVEL);
struct nxp_enet_qos_mdio_config {
const struct device *enet_dev;
};
struct nxp_enet_qos_mdio_data {
struct k_mutex mdio_mutex;
};
struct mdio_transaction {
enum mdio_opcode op;
union {
uint16_t write_data;
uint16_t *read_data;
};
uint8_t portaddr;
uint8_t regaddr;
enet_qos_t *base;
struct k_mutex *mdio_bus_mutex;
};
static bool check_busy(enet_qos_t *base)
{
uint32_t val = base->MAC_MDIO_ADDRESS;
/* Return the busy bit */
return ENET_QOS_REG_GET(MAC_MDIO_ADDRESS, GB, val);
}
static int do_transaction(struct mdio_transaction *mdio)
{
enet_qos_t *base = mdio->base;
uint8_t goc_1_code;
int ret;
k_mutex_lock(mdio->mdio_bus_mutex, K_FOREVER);
if (mdio->op == MDIO_OP_C22_WRITE) {
base->MAC_MDIO_DATA =
/* Prepare the data to be written */
ENET_QOS_REG_PREP(MAC_MDIO_DATA, GD, mdio->write_data);
goc_1_code = 0b0;
} else if (mdio->op == MDIO_OP_C22_READ) {
goc_1_code = 0b1;
} else {
ret = -EINVAL;
goto done;
}
base->MAC_MDIO_ADDRESS =
/* OP command */
ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, GOC_1, goc_1_code) |
ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, GOC_0, 0b1) |
/* PHY address */
ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, PA, mdio->portaddr) |
/* Register address */
ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, RDA, mdio->regaddr);
base->MAC_MDIO_ADDRESS =
/* Start the transaction */
ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, GB, 0b1);
ret = -ETIMEDOUT;
for (int i = CONFIG_MDIO_NXP_ENET_QOS_RECHECK_COUNT; i > 0; i--) {
if (!check_busy(base)) {
ret = 0;
break;
}
k_busy_wait(CONFIG_MDIO_NXP_ENET_QOS_RECHECK_TIME);
}
if (ret) {
LOG_ERR("MDIO transaction timed out");
goto done;
}
if (mdio->op == MDIO_OP_C22_READ) {
uint32_t val = mdio->base->MAC_MDIO_DATA;
*mdio->read_data =
/* Decipher the read data */
ENET_QOS_REG_GET(MAC_MDIO_DATA, GD, val);
}
done:
k_mutex_unlock(mdio->mdio_bus_mutex);
return ret;
}
static int nxp_enet_qos_mdio_read(const struct device *dev,
uint8_t portaddr, uint8_t regaddr,
uint16_t *read_data)
{
const struct nxp_enet_qos_mdio_config *config = dev->config;
struct nxp_enet_qos_mdio_data *data = dev->data;
enet_qos_t *base = ENET_QOS_MODULE_CFG(config->enet_dev)->base;
struct mdio_transaction mdio_read = {
.op = MDIO_OP_C22_READ,
.read_data = read_data,
.portaddr = portaddr,
.regaddr = regaddr,
.base = base,
.mdio_bus_mutex = &data->mdio_mutex,
};
return do_transaction(&mdio_read);
}
static int nxp_enet_qos_mdio_write(const struct device *dev,
uint8_t portaddr, uint8_t regaddr,
uint16_t write_data)
{
const struct nxp_enet_qos_mdio_config *config = dev->config;
struct nxp_enet_qos_mdio_data *data = dev->data;
enet_qos_t *base = ENET_QOS_MODULE_CFG(config->enet_dev)->base;
struct mdio_transaction mdio_write = {
.op = MDIO_OP_C22_WRITE,
.write_data = write_data,
.portaddr = portaddr,
.regaddr = regaddr,
.base = base,
.mdio_bus_mutex = &data->mdio_mutex,
};
return do_transaction(&mdio_write);
}
static void nxp_enet_qos_mdio_bus_fn(const struct device *dev)
{
/* Intentionally empty. IP does not support this functionality. */
ARG_UNUSED(dev);
}
static const struct mdio_driver_api nxp_enet_qos_mdio_api = {
.read = nxp_enet_qos_mdio_read,
.write = nxp_enet_qos_mdio_write,
.bus_enable = nxp_enet_qos_mdio_bus_fn,
.bus_disable = nxp_enet_qos_mdio_bus_fn,
};
static int nxp_enet_qos_mdio_init(const struct device *dev)
{
const struct nxp_enet_qos_mdio_config *mdio_config = dev->config;
struct nxp_enet_qos_mdio_data *data = dev->data;
const struct nxp_enet_qos_config *config = ENET_QOS_MODULE_CFG(mdio_config->enet_dev);
uint32_t enet_module_clk_rate;
int ret, divider;
ret = k_mutex_init(&data->mdio_mutex);
if (ret) {
return ret;
}
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys,
&enet_module_clk_rate);
if (ret) {
return ret;
}
enet_module_clk_rate /= 1000000;
if (enet_module_clk_rate >= 20 && enet_module_clk_rate < 35) {
divider = 2;
} else if (enet_module_clk_rate < 60) {
divider = 3;
} else if (enet_module_clk_rate < 100) {
divider = 0;
} else if (enet_module_clk_rate < 150) {
divider = 1;
} else if (enet_module_clk_rate < 250) {
divider = 4;
} else {
LOG_ERR("ENET QOS clk rate does not allow MDIO");
return -ENOTSUP;
}
config->base->MAC_MDIO_ADDRESS =
/* Configure the MDIO clock range / divider */
ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, CR, divider);
return 0;
}
#define NXP_ENET_QOS_MDIO_INIT(inst) \
\
static const struct nxp_enet_qos_mdio_config \
nxp_enet_qos_mdio_cfg_##inst = { \
.enet_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
}; \
\
static struct nxp_enet_qos_mdio_data nxp_enet_qos_mdio_data_##inst; \
\
DEVICE_DT_INST_DEFINE(inst, &nxp_enet_qos_mdio_init, NULL, \
&nxp_enet_qos_mdio_data_##inst, \
&nxp_enet_qos_mdio_cfg_##inst, \
POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, \
&nxp_enet_qos_mdio_api); \
DT_INST_FOREACH_STATUS_OKAY(NXP_ENET_QOS_MDIO_INIT)

View file

@ -33,6 +33,8 @@ LOG_MODULE_REGISTER(mdio_shell, CONFIG_LOG_DEFAULT_LEVEL);
#define DT_DRV_COMPAT nxp_enet_mdio
#elif DT_HAS_COMPAT_STATUS_OKAY(infineon_xmc4xxx_mdio)
#define DT_DRV_COMPAT infineon_xmc4xxx_mdio
#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_enet_qos_mdio)
#define DT_DRV_COMPAT nxp_enet_qos_mdio
#else
#error "No known devicetree compatible match for MDIO shell"
#endif