already (re-)start autonegotiation in phy_mii_cfg_link. Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
596 lines
15 KiB
C
596 lines
15 KiB
C
/*
|
|
* Copyright (c) 2021 IP-Logix Inc.
|
|
* Copyright 2022 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT ethernet_phy
|
|
|
|
#include <errno.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/drivers/mdio.h>
|
|
#include <zephyr/net/phy.h>
|
|
#include <zephyr/net/mii.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(phy_mii, CONFIG_PHY_LOG_LEVEL);
|
|
|
|
#include "phy_mii.h"
|
|
|
|
#define ANY_DYNAMIC_LINK UTIL_NOT(DT_ALL_INST_HAS_PROP_STATUS_OKAY(fixed_link))
|
|
#define ANY_FIXED_LINK DT_ANY_INST_HAS_PROP_STATUS_OKAY(fixed_link)
|
|
|
|
struct phy_mii_dev_config {
|
|
uint8_t phy_addr;
|
|
bool no_reset;
|
|
bool fixed;
|
|
int fixed_speed;
|
|
enum phy_link_speed default_speeds;
|
|
const struct device * const mdio;
|
|
};
|
|
|
|
struct phy_mii_dev_data {
|
|
const struct device *dev;
|
|
phy_callback_t cb;
|
|
void *cb_data;
|
|
struct phy_link_state state;
|
|
struct k_sem sem;
|
|
#if ANY_DYNAMIC_LINK
|
|
struct k_work_delayable monitor_work;
|
|
bool gigabit_supported;
|
|
bool autoneg_in_progress;
|
|
k_timepoint_t autoneg_timeout;
|
|
#endif
|
|
};
|
|
|
|
/* Offset to align capabilities bits of 1000BASE-T Control and Status regs */
|
|
#define MII_1KSTSR_OFFSET 2
|
|
|
|
#define MII_INVALID_PHY_ID UINT32_MAX
|
|
|
|
/* How often to poll auto-negotiation status while waiting for it to complete */
|
|
#define MII_AUTONEG_POLL_INTERVAL_MS 100
|
|
|
|
static void invoke_link_cb(const struct device *dev);
|
|
|
|
#if ANY_DYNAMIC_LINK
|
|
static inline int phy_mii_reg_read(const struct device *dev, uint16_t reg_addr,
|
|
uint16_t *value)
|
|
{
|
|
const struct phy_mii_dev_config *const cfg = dev->config;
|
|
|
|
/* if there is no mdio (fixed-link) it is not supported to read */
|
|
if (cfg->fixed) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (cfg->mdio == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
return mdio_read(cfg->mdio, cfg->phy_addr, reg_addr, value);
|
|
}
|
|
|
|
static inline int phy_mii_reg_write(const struct device *dev, uint16_t reg_addr,
|
|
uint16_t value)
|
|
{
|
|
const struct phy_mii_dev_config *const cfg = dev->config;
|
|
|
|
/* if there is no mdio (fixed-link) it is not supported to write */
|
|
if (cfg->fixed) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (cfg->mdio == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
return mdio_write(cfg->mdio, cfg->phy_addr, reg_addr, value);
|
|
}
|
|
|
|
static int read_gigabit_supported_flag(const struct device *dev, bool *supported)
|
|
{
|
|
uint16_t bmsr_reg;
|
|
uint16_t estat_reg;
|
|
|
|
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if ((bmsr_reg & MII_BMSR_EXTEND_STATUS) != 0U) {
|
|
if (phy_mii_reg_read(dev, MII_ESTAT, &estat_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if ((estat_reg & (MII_ESTAT_1000BASE_T_HALF | MII_ESTAT_1000BASE_T_FULL)) != 0U) {
|
|
*supported = true;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
*supported = false;
|
|
return 0;
|
|
}
|
|
|
|
static int reset(const struct device *dev)
|
|
{
|
|
uint32_t timeout = 12U;
|
|
uint16_t value;
|
|
|
|
/* Issue a soft reset */
|
|
if (phy_mii_reg_write(dev, MII_BMCR, MII_BMCR_RESET) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* Wait up to 0.6s for the reset sequence to finish. According to
|
|
* IEEE 802.3, Section 2, Subsection 22.2.4.1.1 a PHY reset may take
|
|
* up to 0.5 s.
|
|
*/
|
|
do {
|
|
if (timeout-- == 0U) {
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
k_sleep(K_MSEC(50));
|
|
|
|
if (phy_mii_reg_read(dev, MII_BMCR, &value) < 0) {
|
|
return -EIO;
|
|
}
|
|
} while ((value & MII_BMCR_RESET) != 0U);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_id(const struct device *dev, uint32_t *phy_id)
|
|
{
|
|
uint16_t value;
|
|
|
|
if (phy_mii_reg_read(dev, MII_PHYID1R, &value) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
*phy_id = value << 16;
|
|
|
|
if (phy_mii_reg_read(dev, MII_PHYID2R, &value) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
*phy_id |= value;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int update_link_state(const struct device *dev)
|
|
{
|
|
const struct phy_mii_dev_config *const cfg = dev->config;
|
|
struct phy_mii_dev_data *const data = dev->data;
|
|
bool link_up;
|
|
|
|
uint16_t bmcr_reg = 0;
|
|
uint16_t bmsr_reg = 0;
|
|
|
|
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
link_up = (bmsr_reg & MII_BMSR_LINK_STATUS) != 0U;
|
|
|
|
/* If link is down, we can stop here. */
|
|
if (!link_up) {
|
|
data->state.speed = 0;
|
|
if (link_up != data->state.is_up) {
|
|
data->state.is_up = false;
|
|
LOG_INF("PHY (%d) is down", cfg->phy_addr);
|
|
return 0;
|
|
}
|
|
return -EAGAIN;
|
|
}
|
|
|
|
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* If auto-negotiation is not enabled, we only need to check the link speed */
|
|
if ((bmcr_reg & MII_BMCR_AUTONEG_ENABLE) == 0U) {
|
|
enum phy_link_speed new_speed = phy_mii_get_link_speed_bmcr_reg(dev, bmcr_reg);
|
|
|
|
if ((data->state.speed != new_speed) || !data->state.is_up) {
|
|
data->state.is_up = true;
|
|
data->state.speed = new_speed;
|
|
|
|
LOG_INF("PHY (%d) Link speed %s Mb, %s duplex",
|
|
cfg->phy_addr,
|
|
PHY_LINK_IS_SPEED_1000M(data->state.speed) ? "1000" :
|
|
(PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" : "10"),
|
|
PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half");
|
|
|
|
return 0;
|
|
}
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* If auto-negotiation is enabled and the link was already up last time we checked,
|
|
* we can return immediately, as the link state has not changed.
|
|
* If the link was down, we will start the auto-negotiation sequence.
|
|
*/
|
|
if (data->state.is_up) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
data->state.is_up = true;
|
|
|
|
LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence", cfg->phy_addr);
|
|
|
|
data->autoneg_timeout = sys_timepoint_calc(K_MSEC(CONFIG_PHY_AUTONEG_TIMEOUT_MS));
|
|
return -EINPROGRESS;
|
|
}
|
|
|
|
static int check_autonegotiation_completion(const struct device *dev)
|
|
{
|
|
const struct phy_mii_dev_config *const cfg = dev->config;
|
|
struct phy_mii_dev_data *const data = dev->data;
|
|
|
|
uint16_t anar_reg = 0;
|
|
uint16_t bmsr_reg = 0;
|
|
uint16_t anlpar_reg = 0;
|
|
uint16_t c1kt_reg = 0;
|
|
uint16_t s1kt_reg = 0;
|
|
|
|
/* On some PHY chips, the BMSR bits are latched, so the first read may
|
|
* show incorrect status. A second read ensures correct values.
|
|
*/
|
|
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* Second read, clears the latched bits and gives the correct status */
|
|
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if ((bmsr_reg & MII_BMSR_AUTONEG_COMPLETE) == 0U) {
|
|
if (sys_timepoint_expired(data->autoneg_timeout)) {
|
|
LOG_DBG("PHY (%d) auto-negotiate timeout", cfg->phy_addr);
|
|
return -ETIMEDOUT;
|
|
}
|
|
return -EINPROGRESS;
|
|
}
|
|
|
|
LOG_DBG("PHY (%d) auto-negotiate sequence completed",
|
|
cfg->phy_addr);
|
|
|
|
/* Read PHY default advertising parameters */
|
|
if (phy_mii_reg_read(dev, MII_ANAR, &anar_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
/** Read peer device capability */
|
|
if (phy_mii_reg_read(dev, MII_ANLPAR, &anlpar_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (data->gigabit_supported) {
|
|
if (phy_mii_reg_read(dev, MII_1KTCR, &c1kt_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
if (phy_mii_reg_read(dev, MII_1KSTSR, &s1kt_reg) < 0) {
|
|
return -EIO;
|
|
}
|
|
s1kt_reg = (uint16_t)(s1kt_reg >> MII_1KSTSR_OFFSET);
|
|
}
|
|
|
|
if (data->gigabit_supported &&
|
|
((c1kt_reg & s1kt_reg & MII_ADVERTISE_1000_FULL) != 0U)) {
|
|
data->state.speed = LINK_FULL_1000BASE;
|
|
} else if (data->gigabit_supported &&
|
|
((c1kt_reg & s1kt_reg & MII_ADVERTISE_1000_HALF) != 0U)) {
|
|
data->state.speed = LINK_HALF_1000BASE;
|
|
} else if ((anar_reg & anlpar_reg & MII_ADVERTISE_100_FULL) != 0U) {
|
|
data->state.speed = LINK_FULL_100BASE;
|
|
} else if ((anar_reg & anlpar_reg & MII_ADVERTISE_100_HALF) != 0U) {
|
|
data->state.speed = LINK_HALF_100BASE;
|
|
} else if ((anar_reg & anlpar_reg & MII_ADVERTISE_10_FULL) != 0U) {
|
|
data->state.speed = LINK_FULL_10BASE;
|
|
} else {
|
|
data->state.speed = LINK_HALF_10BASE;
|
|
}
|
|
|
|
data->state.is_up = (bmsr_reg & MII_BMSR_LINK_STATUS) != 0U;
|
|
|
|
LOG_INF("PHY (%d) Link speed %s Mb, %s duplex",
|
|
cfg->phy_addr,
|
|
PHY_LINK_IS_SPEED_1000M(data->state.speed) ? "1000" :
|
|
(PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" : "10"),
|
|
PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void monitor_work_handler(struct k_work *work)
|
|
{
|
|
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
|
struct phy_mii_dev_data *const data =
|
|
CONTAINER_OF(dwork, struct phy_mii_dev_data, monitor_work);
|
|
const struct device *dev = data->dev;
|
|
int rc;
|
|
|
|
if (k_sem_take(&data->sem, K_NO_WAIT) == 0) {
|
|
if (data->autoneg_in_progress) {
|
|
rc = check_autonegotiation_completion(dev);
|
|
} else {
|
|
/* If autonegotiation is not in progress, just update the link state */
|
|
rc = update_link_state(dev);
|
|
}
|
|
|
|
data->autoneg_in_progress = (rc == -EINPROGRESS);
|
|
|
|
k_sem_give(&data->sem);
|
|
|
|
/* If link state has changed and a callback is set, invoke callback */
|
|
if (rc == 0) {
|
|
invoke_link_cb(dev);
|
|
}
|
|
}
|
|
|
|
k_work_reschedule(&data->monitor_work, data->autoneg_in_progress
|
|
? K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS)
|
|
: K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
|
|
}
|
|
|
|
static int phy_mii_read(const struct device *dev, uint16_t reg_addr,
|
|
uint32_t *data)
|
|
{
|
|
return phy_mii_reg_read(dev, reg_addr, (uint16_t *)data);
|
|
}
|
|
|
|
static int phy_mii_write(const struct device *dev, uint16_t reg_addr,
|
|
uint32_t data)
|
|
{
|
|
return phy_mii_reg_write(dev, reg_addr, (uint16_t)data);
|
|
}
|
|
|
|
static int phy_mii_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds,
|
|
enum phy_cfg_link_flag flags)
|
|
{
|
|
struct phy_mii_dev_data *const data = dev->data;
|
|
const struct phy_mii_dev_config *const cfg = dev->config;
|
|
int ret = 0;
|
|
|
|
/* if there is no mdio (fixed-link) it is not supported to configure link */
|
|
if (cfg->fixed) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (cfg->mdio == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
k_sem_take(&data->sem, K_FOREVER);
|
|
|
|
if ((flags & PHY_FLAG_AUTO_NEGOTIATION_DISABLED) != 0U) {
|
|
/* If auto-negotiation is disabled, only one speed can be selected.
|
|
* If gigabit is not supported, this speed must not be 1000M.
|
|
*/
|
|
if (!data->gigabit_supported && PHY_LINK_IS_SPEED_1000M(adv_speeds)) {
|
|
LOG_ERR("PHY (%d) Gigabit not supported, can't configure link",
|
|
cfg->phy_addr);
|
|
ret = -ENOTSUP;
|
|
goto cfg_link_end;
|
|
}
|
|
|
|
ret = phy_mii_set_bmcr_reg_autoneg_disabled(dev, adv_speeds);
|
|
if (ret >= 0) {
|
|
data->autoneg_in_progress = false;
|
|
k_work_reschedule(&data->monitor_work, K_NO_WAIT);
|
|
}
|
|
} else {
|
|
ret = phy_mii_cfg_link_autoneg(dev, adv_speeds, data->gigabit_supported);
|
|
if (ret >= 0) {
|
|
LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence", cfg->phy_addr);
|
|
data->autoneg_in_progress = true;
|
|
data->autoneg_timeout =
|
|
sys_timepoint_calc(K_MSEC(CONFIG_PHY_AUTONEG_TIMEOUT_MS));
|
|
k_work_reschedule(&data->monitor_work,
|
|
K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
|
|
}
|
|
}
|
|
|
|
if (ret == -EALREADY) {
|
|
LOG_DBG("PHY (%d) Link already configured", cfg->phy_addr);
|
|
}
|
|
|
|
cfg_link_end:
|
|
k_sem_give(&data->sem);
|
|
|
|
return ret;
|
|
}
|
|
#endif /* ANY_DYNAMIC_LINK */
|
|
|
|
static int phy_mii_get_link_state(const struct device *dev,
|
|
struct phy_link_state *state)
|
|
{
|
|
struct phy_mii_dev_data *const data = dev->data;
|
|
|
|
k_sem_take(&data->sem, K_FOREVER);
|
|
|
|
memcpy(state, &data->state, sizeof(struct phy_link_state));
|
|
|
|
if (state->speed == 0) {
|
|
/* If speed is 0, then link is also down, happens when autonegotiation is in
|
|
* progress
|
|
*/
|
|
state->is_up = false;
|
|
}
|
|
|
|
k_sem_give(&data->sem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void invoke_link_cb(const struct device *dev)
|
|
{
|
|
struct phy_mii_dev_data *const data = dev->data;
|
|
struct phy_link_state state;
|
|
|
|
if (data->cb == NULL) {
|
|
return;
|
|
}
|
|
|
|
phy_mii_get_link_state(dev, &state);
|
|
|
|
data->cb(dev, &state, data->cb_data);
|
|
}
|
|
|
|
static int phy_mii_link_cb_set(const struct device *dev, phy_callback_t cb,
|
|
void *user_data)
|
|
{
|
|
struct phy_mii_dev_data *const data = dev->data;
|
|
|
|
data->cb = cb;
|
|
data->cb_data = user_data;
|
|
|
|
/**
|
|
* Immediately invoke the callback to notify the caller of the
|
|
* current link status.
|
|
*/
|
|
invoke_link_cb(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if ANY_FIXED_LINK
|
|
static int phy_mii_initialize_fixed_link(const struct device *dev)
|
|
{
|
|
const struct phy_mii_dev_config *const cfg = dev->config;
|
|
struct phy_mii_dev_data *const data = dev->data;
|
|
|
|
/**
|
|
* If this is a *fixed* link then we don't need to communicate
|
|
* with a PHY. We set the link parameters as configured
|
|
* and set link state to up.
|
|
*/
|
|
|
|
const static int speed_to_phy_link_speed[] = {
|
|
LINK_HALF_10BASE,
|
|
LINK_FULL_10BASE,
|
|
LINK_HALF_100BASE,
|
|
LINK_FULL_100BASE,
|
|
LINK_HALF_1000BASE,
|
|
LINK_FULL_1000BASE,
|
|
};
|
|
|
|
data->state.speed = speed_to_phy_link_speed[cfg->fixed_speed];
|
|
data->state.is_up = true;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* ANY_FIXED_LINK */
|
|
|
|
#if ANY_DYNAMIC_LINK
|
|
static int phy_mii_initialize_dynamic_link(const struct device *dev)
|
|
{
|
|
const struct phy_mii_dev_config *const cfg = dev->config;
|
|
struct phy_mii_dev_data *const data = dev->data;
|
|
uint32_t phy_id;
|
|
int ret = 0;
|
|
|
|
data->state.is_up = false;
|
|
|
|
mdio_bus_enable(cfg->mdio);
|
|
|
|
if (cfg->no_reset == false) {
|
|
ret = reset(dev);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to reset PHY (%d): %d", cfg->phy_addr, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (get_id(dev, &phy_id) == 0) {
|
|
if (phy_id == MII_INVALID_PHY_ID) {
|
|
LOG_ERR("No PHY found at address %d", cfg->phy_addr);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOG_INF("PHY (%d) ID %X", cfg->phy_addr, phy_id);
|
|
}
|
|
|
|
ret = read_gigabit_supported_flag(dev, &data->gigabit_supported);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to read PHY capabilities: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
k_work_init_delayable(&data->monitor_work, monitor_work_handler);
|
|
|
|
/* Advertise default speeds */
|
|
phy_mii_cfg_link(dev, cfg->default_speeds, 0);
|
|
|
|
/* This will schedule the monitor work, if not already scheduled by phy_mii_cfg_link(). */
|
|
k_work_schedule(&data->monitor_work, K_NO_WAIT);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* ANY_DYNAMIC_LINK */
|
|
|
|
#define IS_FIXED_LINK(n) DT_INST_NODE_HAS_PROP(n, fixed_link)
|
|
|
|
static DEVICE_API(ethphy, phy_mii_driver_api) = {
|
|
.get_link = phy_mii_get_link_state,
|
|
.link_cb_set = phy_mii_link_cb_set,
|
|
#if ANY_DYNAMIC_LINK
|
|
.cfg_link = phy_mii_cfg_link,
|
|
.read = phy_mii_read,
|
|
.write = phy_mii_write,
|
|
#endif
|
|
};
|
|
|
|
#define PHY_MII_GENERATE_DEFAULT_SPEEDS(n) \
|
|
((DT_INST_ENUM_HAS_VALUE(n, default_speeds, 10base_half_duplex) ? LINK_HALF_10BASE : 0) | \
|
|
(DT_INST_ENUM_HAS_VALUE(n, default_speeds, 10base_full_duplex) ? LINK_FULL_10BASE : 0) | \
|
|
(DT_INST_ENUM_HAS_VALUE(n, default_speeds, 100base_half_duplex) ? LINK_HALF_100BASE : 0) | \
|
|
(DT_INST_ENUM_HAS_VALUE(n, default_speeds, 100base_full_duplex) ? LINK_FULL_100BASE : 0) | \
|
|
(DT_INST_ENUM_HAS_VALUE(n, default_speeds, 1000base_half_duplex) ? LINK_HALF_1000BASE : 0) | \
|
|
(DT_INST_ENUM_HAS_VALUE(n, default_speeds, 1000base_full_duplex) ? LINK_FULL_1000BASE : 0))
|
|
|
|
#define PHY_MII_CONFIG(n) \
|
|
BUILD_ASSERT(PHY_MII_GENERATE_DEFAULT_SPEEDS(n) != 0, \
|
|
"At least one valid speed must be configured for this driver"); \
|
|
\
|
|
static const struct phy_mii_dev_config phy_mii_dev_config_##n = { \
|
|
.phy_addr = DT_INST_REG_ADDR(n), \
|
|
.no_reset = DT_INST_PROP(n, no_reset), \
|
|
.fixed = IS_FIXED_LINK(n), \
|
|
.fixed_speed = DT_INST_ENUM_IDX_OR(n, fixed_link, 0), \
|
|
.default_speeds = PHY_MII_GENERATE_DEFAULT_SPEEDS(n), \
|
|
.mdio = UTIL_AND(UTIL_NOT(IS_FIXED_LINK(n)), \
|
|
DEVICE_DT_GET(DT_INST_BUS(n))) \
|
|
};
|
|
|
|
#define PHY_MII_DATA(n) \
|
|
static struct phy_mii_dev_data phy_mii_dev_data_##n = { \
|
|
.dev = DEVICE_DT_INST_GET(n), \
|
|
.cb = NULL, \
|
|
.sem = Z_SEM_INITIALIZER(phy_mii_dev_data_##n.sem, 1, 1), \
|
|
};
|
|
|
|
#define PHY_MII_INIT(n) \
|
|
COND_CODE_1(IS_FIXED_LINK(n), (&phy_mii_initialize_fixed_link), \
|
|
(&phy_mii_initialize_dynamic_link))
|
|
|
|
#define PHY_MII_DEVICE(n) \
|
|
PHY_MII_CONFIG(n); \
|
|
PHY_MII_DATA(n); \
|
|
DEVICE_DT_INST_DEFINE(n, \
|
|
PHY_MII_INIT(n), \
|
|
NULL, \
|
|
&phy_mii_dev_data_##n, \
|
|
&phy_mii_dev_config_##n, POST_KERNEL, \
|
|
CONFIG_PHY_INIT_PRIORITY, \
|
|
&phy_mii_driver_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(PHY_MII_DEVICE)
|