drivers: Add Ethernet PHY API
This commit adds support for Ethernet PHY drivers via a PHY API. It also includes a driver for a generic MII compliant PHY which supports most PHYs on the market. Separating PHY driver from the SoC specific Ethernet driver simplifies the Ethernet driver code and enables code re-use. Drivers for specific PHYs with more advanced features, such as RGMII delay in PHY can be developed independent of the Ethernet MAC driver. Signed-off-by: Arvin Farahmand <arvinf@ip-logix.com>
This commit is contained in:
parent
2bec7587e9
commit
f845cddcf7
|
@ -222,6 +222,7 @@
|
|||
/drivers/ethernet/*stm32* @Nukersson @lochej
|
||||
/drivers/ethernet/*w5500* @parthitce
|
||||
/drivers/ethernet/*xlnx_gem* @ibirnbaum
|
||||
/drivers/ethernet/phy/ @rlubos @tbursztyka @arvinf
|
||||
/drivers/mdio/ @rlubos @tbursztyka @arvinf
|
||||
/drivers/flash/ @nashif @nvlsianpu
|
||||
/drivers/flash/*b91* @yurvyn
|
||||
|
|
|
@ -40,3 +40,5 @@ if(CONFIG_ETH_NATIVE_POSIX)
|
|||
eth_native_posix_adapt.c
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory(phy)
|
||||
|
|
|
@ -59,4 +59,6 @@ source "drivers/ethernet/Kconfig.w5500"
|
|||
source "drivers/ethernet/Kconfig.dsa"
|
||||
source "drivers/ethernet/Kconfig.xlnx_gem"
|
||||
|
||||
source "drivers/ethernet/phy/Kconfig"
|
||||
|
||||
endmenu # "Ethernet Drivers"
|
||||
|
|
5
drivers/ethernet/phy/CMakeLists.txt
Normal file
5
drivers/ethernet/phy/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_PHY_GENERIC_MII phy_mii.c)
|
47
drivers/ethernet/phy/Kconfig
Normal file
47
drivers/ethernet/phy/Kconfig
Normal file
|
@ -0,0 +1,47 @@
|
|||
# Ethernet PHY drivers configuration options
|
||||
|
||||
# Copyright (c) 2021 IP-Logix Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menu "Ethernet PHY Drivers"
|
||||
depends on NET_L2_ETHERNET
|
||||
|
||||
module = PHY
|
||||
module-dep = LOG
|
||||
module-str = Log level for Ethernet PHY driver
|
||||
module-help = Sets log level for Ethernet PHY Device Drivers.
|
||||
source "subsys/net/Kconfig.template.log_config.net"
|
||||
|
||||
config PHY_INIT_PRIORITY
|
||||
int "Ethernet PHY driver init priority"
|
||||
default 70
|
||||
help
|
||||
Ethernet PHY device driver initialization priority.
|
||||
Do not mess with it unless you know what you are doing.
|
||||
Note that the priority needs to be lower than the net stack
|
||||
so that it can start before the networking sub-system.
|
||||
|
||||
config PHY_GENERIC_MII
|
||||
bool "Generic MII PHY Driver"
|
||||
default y
|
||||
depends on MDIO
|
||||
help
|
||||
This is a generic MII PHY interface that communicates with the
|
||||
PHY using the MDIO bus.
|
||||
|
||||
config PHY_AUTONEG_TIMEOUT_MS
|
||||
int "Auto-negotiation timeout value in milliseconds"
|
||||
default 4000
|
||||
help
|
||||
Maximum duration of auto-negotiation sequence in milliseconds
|
||||
before the process fails
|
||||
|
||||
config PHY_MONITOR_PERIOD
|
||||
int "Monitor task execution period"
|
||||
default 500
|
||||
help
|
||||
Monitor task execution period in milliseconds. The monitor task is
|
||||
periodically executed to detect and report any changes in the
|
||||
PHY link status to the operating system.
|
||||
|
||||
endmenu # "Ethernet PHY Drivers"
|
422
drivers/ethernet/phy/phy_mii.c
Normal file
422
drivers/ethernet/phy/phy_mii.c
Normal file
|
@ -0,0 +1,422 @@
|
|||
/*
|
||||
* Copyright (c) 2021 IP-Logix Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT ethernet_phy
|
||||
|
||||
#include <errno.h>
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
#include <soc.h>
|
||||
#include <drivers/mdio.h>
|
||||
#include <net/phy.h>
|
||||
#include <net/mii.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(phy_mii, CONFIG_PHY_LOG_LEVEL);
|
||||
|
||||
struct phy_mii_dev_config {
|
||||
uint8_t phy_addr;
|
||||
bool no_reset;
|
||||
bool fixed;
|
||||
int fixed_speed;
|
||||
const struct device * const mdio;
|
||||
};
|
||||
|
||||
struct phy_mii_dev_data {
|
||||
const struct device *dev;
|
||||
phy_callback_t cb;
|
||||
void *cb_data;
|
||||
struct k_work_delayable monitor_work;
|
||||
struct phy_link_state state;
|
||||
struct k_sem sem;
|
||||
};
|
||||
|
||||
#define DEV_NAME(dev) ((dev)->name)
|
||||
#define DEV_DATA(dev) ((struct phy_mii_dev_data *const)(dev)->data)
|
||||
#define DEV_CFG(dev) \
|
||||
((const struct phy_mii_dev_config *const)(dev)->config)
|
||||
|
||||
static int phy_mii_get_link_state(const struct device *dev,
|
||||
struct phy_link_state *state);
|
||||
|
||||
static inline int reg_read(const struct device *dev, uint16_t reg_addr,
|
||||
uint16_t *value)
|
||||
{
|
||||
const struct phy_mii_dev_config *const cfg = DEV_CFG(dev);
|
||||
|
||||
return mdio_read(cfg->mdio, cfg->phy_addr, reg_addr, value);
|
||||
}
|
||||
|
||||
static inline int reg_write(const struct device *dev, uint16_t reg_addr,
|
||||
uint16_t value)
|
||||
{
|
||||
const struct phy_mii_dev_config *const cfg = DEV_CFG(dev);
|
||||
|
||||
return mdio_write(cfg->mdio, cfg->phy_addr, reg_addr, value);
|
||||
}
|
||||
|
||||
static int reset(const struct device *dev)
|
||||
{
|
||||
uint32_t timeout = 12U;
|
||||
uint16_t value;
|
||||
|
||||
/* Issue a soft reset */
|
||||
if (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 (reg_read(dev, MII_BMCR, &value) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
} while (value & MII_BMCR_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_id(const struct device *dev, uint32_t *phy_id)
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
if (reg_read(dev, MII_PHYID1R, &value) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*phy_id = (value & 0xFFFF) << 16;
|
||||
|
||||
if (reg_read(dev, MII_PHYID2R, &value) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*phy_id |= (value & 0xFFFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_link_state(const struct device *dev)
|
||||
{
|
||||
const struct phy_mii_dev_config *const cfg = DEV_CFG(dev);
|
||||
struct phy_mii_dev_data *const data = DEV_DATA(dev);
|
||||
bool link_up;
|
||||
|
||||
uint16_t anar_reg = 0;
|
||||
uint16_t bmcr_reg = 0;
|
||||
uint16_t bmsr_reg = 0;
|
||||
uint16_t anlpar_reg = 0;
|
||||
uint32_t timeout = CONFIG_PHY_AUTONEG_TIMEOUT_MS / 100;
|
||||
|
||||
if (reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
link_up = bmsr_reg & MII_BMSR_LINK_STATUS;
|
||||
|
||||
/* If there is no change in link state don't proceed. */
|
||||
if (link_up == data->state.is_up) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
data->state.is_up = link_up;
|
||||
|
||||
/* If link is down, there is nothing more to be done */
|
||||
if (data->state.is_up == false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform auto-negotiation sequence.
|
||||
*/
|
||||
LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence",
|
||||
cfg->phy_addr);
|
||||
|
||||
/* Read PHY default advertising parameters */
|
||||
if (reg_read(dev, MII_ANAR, &anar_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Configure and start auto-negotiation process */
|
||||
if (reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
bmcr_reg |= MII_BMCR_AUTONEG_ENABLE | MII_BMCR_AUTONEG_RESTART;
|
||||
bmcr_reg &= ~MII_BMCR_ISOLATE; /* Don't isolate the PHY */
|
||||
|
||||
if (reg_write(dev, MII_BMCR, bmcr_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Wait for the auto-negotiation process to complete */
|
||||
do {
|
||||
if (timeout-- == 0U) {
|
||||
LOG_DBG("PHY (%d) auto-negotiate timedout",
|
||||
cfg->phy_addr);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
k_sleep(K_MSEC(100));
|
||||
|
||||
if (reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
} while (!(bmsr_reg & MII_BMSR_AUTONEG_COMPLETE));
|
||||
|
||||
LOG_DBG("PHY (%d) auto-negotiate sequence completed",
|
||||
cfg->phy_addr);
|
||||
|
||||
/** Read peer device capability */
|
||||
if (reg_read(dev, MII_ANLPAR, &anlpar_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Determine best link speed */
|
||||
if ((anar_reg & anlpar_reg) & MII_ADVERTISE_100_FULL) {
|
||||
data->state.speed = LINK_FULL_100BASE_T;
|
||||
} else if ((anar_reg & anlpar_reg) & MII_ADVERTISE_100_HALF) {
|
||||
data->state.speed = LINK_HALF_100BASE_T;
|
||||
} else if ((anar_reg & anlpar_reg) & MII_ADVERTISE_10_FULL) {
|
||||
data->state.speed = LINK_FULL_10BASE_T;
|
||||
} else {
|
||||
data->state.speed = LINK_HALF_10BASE_T;
|
||||
}
|
||||
|
||||
LOG_INF("PHY (%d) Link speed %s Mb, %s duplex",
|
||||
cfg->phy_addr,
|
||||
PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" : "10",
|
||||
PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void invoke_link_cb(const struct device *dev)
|
||||
{
|
||||
struct phy_mii_dev_data *const data = DEV_DATA(dev);
|
||||
struct phy_link_state state;
|
||||
|
||||
if (data->cb == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
phy_mii_get_link_state(dev, &state);
|
||||
|
||||
data->cb(data->dev, &state, data->cb_data);
|
||||
}
|
||||
|
||||
static void monitor_work_handler(struct k_work *work)
|
||||
{
|
||||
struct phy_mii_dev_data *const data =
|
||||
CONTAINER_OF(work, struct phy_mii_dev_data, monitor_work);
|
||||
const struct device *dev = data->dev;
|
||||
int rc;
|
||||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
rc = update_link_state(dev);
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
/* If link state has changed and a callback is set, invoke callback */
|
||||
if (rc == 0) {
|
||||
invoke_link_cb(dev);
|
||||
}
|
||||
|
||||
/* Submit delayed work */
|
||||
k_work_reschedule(&data->monitor_work,
|
||||
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
|
||||
}
|
||||
|
||||
static int phy_mii_read(const struct device *dev, uint16_t reg_addr,
|
||||
uint32_t *data)
|
||||
{
|
||||
return 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 reg_write(dev, reg_addr, (uint16_t)data);
|
||||
}
|
||||
|
||||
static int phy_mii_cfg_link(const struct device *dev,
|
||||
enum phy_link_speed adv_speeds)
|
||||
{
|
||||
uint16_t anar_reg;
|
||||
uint16_t bmcr_reg;
|
||||
|
||||
if (reg_read(dev, MII_ANAR, &anar_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (adv_speeds & LINK_FULL_10BASE_T)
|
||||
anar_reg |= MII_ADVERTISE_10_FULL;
|
||||
else
|
||||
anar_reg &= ~MII_ADVERTISE_10_FULL;
|
||||
|
||||
if (adv_speeds & LINK_HALF_10BASE_T)
|
||||
anar_reg |= MII_ADVERTISE_10_HALF;
|
||||
else
|
||||
anar_reg &= ~MII_ADVERTISE_10_HALF;
|
||||
|
||||
if (adv_speeds & LINK_FULL_100BASE_T)
|
||||
anar_reg |= MII_ADVERTISE_100_FULL;
|
||||
else
|
||||
anar_reg &= ~MII_ADVERTISE_100_FULL;
|
||||
|
||||
if (adv_speeds & LINK_HALF_100BASE_T)
|
||||
anar_reg |= MII_ADVERTISE_100_HALF;
|
||||
else
|
||||
anar_reg &= ~MII_ADVERTISE_100_HALF;
|
||||
|
||||
bmcr_reg |= MII_BMCR_AUTONEG_ENABLE;
|
||||
|
||||
if (reg_write(dev, MII_ANAR, anar_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (reg_write(dev, MII_BMCR, bmcr_reg) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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(dev);
|
||||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
memcpy(state, &data->state, sizeof(struct phy_link_state));
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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(dev);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int phy_mii_initialize(const struct device *dev)
|
||||
{
|
||||
const struct phy_mii_dev_config *const cfg = DEV_CFG(dev);
|
||||
struct phy_mii_dev_data *const data = DEV_DATA(dev);
|
||||
uint32_t phy_id;
|
||||
|
||||
k_sem_init(&data->sem, 1, 1);
|
||||
|
||||
data->dev = dev;
|
||||
data->cb = NULL;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
if (cfg->fixed) {
|
||||
const static int speed_to_phy_link_speed[] = {
|
||||
LINK_HALF_10BASE_T,
|
||||
LINK_FULL_10BASE_T,
|
||||
LINK_HALF_100BASE_T,
|
||||
LINK_FULL_100BASE_T,
|
||||
};
|
||||
|
||||
data->state.speed = speed_to_phy_link_speed[cfg->fixed_speed];
|
||||
data->state.is_up = true;
|
||||
} else {
|
||||
data->state.is_up = false;
|
||||
|
||||
mdio_bus_enable(cfg->mdio);
|
||||
|
||||
if (cfg->no_reset == false) {
|
||||
reset(dev);
|
||||
}
|
||||
|
||||
if (get_id(dev, &phy_id) == 0) {
|
||||
if (phy_id == 0xFFFFFF) {
|
||||
LOG_ERR("No PHY found at address %d",
|
||||
cfg->phy_addr);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOG_INF("PHY (%d) ID %X", cfg->phy_addr, phy_id);
|
||||
}
|
||||
|
||||
/* Advertise all speeds */
|
||||
phy_mii_cfg_link(dev, LINK_HALF_10BASE_T |
|
||||
LINK_FULL_10BASE_T |
|
||||
LINK_HALF_100BASE_T |
|
||||
LINK_FULL_100BASE_T);
|
||||
|
||||
k_work_init_delayable(&data->monitor_work,
|
||||
monitor_work_handler);
|
||||
|
||||
monitor_work_handler(&data->monitor_work.work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define IS_FIXED_LINK(n) DT_NODE_HAS_PROP(DT_DRV_INST(n), fixed_link)
|
||||
|
||||
static const struct ethphy_driver_api phy_mii_driver_api = {
|
||||
.get_link = phy_mii_get_link_state,
|
||||
.cfg_link = phy_mii_cfg_link,
|
||||
.link_cb_set = phy_mii_link_cb_set,
|
||||
.read = phy_mii_read,
|
||||
.write = phy_mii_write,
|
||||
};
|
||||
|
||||
#define PHY_MII_CONFIG(n) \
|
||||
static const struct phy_mii_dev_config phy_mii_dev_config_##n = { \
|
||||
.phy_addr = DT_PROP(DT_DRV_INST(n), address), \
|
||||
.fixed = IS_FIXED_LINK(n), \
|
||||
.fixed_speed = DT_ENUM_IDX_OR(DT_DRV_INST(n), fixed_link, 0), \
|
||||
.mdio = UTIL_AND(UTIL_NOT(IS_FIXED_LINK(n)), \
|
||||
DEVICE_DT_GET(DT_PHANDLE(DT_DRV_INST(n), mdio)))\
|
||||
};
|
||||
|
||||
#define PHY_MII_DEVICE(n) \
|
||||
PHY_MII_CONFIG(n); \
|
||||
static struct phy_mii_dev_data phy_mii_dev_data_##n; \
|
||||
DEVICE_DT_INST_DEFINE(n, \
|
||||
&phy_mii_initialize, \
|
||||
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)
|
33
dts/bindings/ethernet/ethernet-phy.yaml
Normal file
33
dts/bindings/ethernet/ethernet-phy.yaml
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) 2021 IP-Logix Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Common fields for MIIPHY devices
|
||||
|
||||
description: Generic MII PHY
|
||||
|
||||
compatible: "ethernet-phy"
|
||||
|
||||
include: phy.yaml
|
||||
|
||||
properties:
|
||||
address:
|
||||
type: int
|
||||
required: true
|
||||
description: PHY address
|
||||
mdio:
|
||||
type: phandle
|
||||
required: true
|
||||
description: MDIO driver node
|
||||
no-reset:
|
||||
type: boolean
|
||||
required: false
|
||||
description: Do not reset the PHY during initialization
|
||||
fixed-link:
|
||||
type: string
|
||||
required: false
|
||||
description: This link is fixed and does not require PHY configuration
|
||||
enum:
|
||||
- "10BASE-T Half-Duplex"
|
||||
- "10BASE-T Full-Duplex"
|
||||
- "100BASE-T Half-Duplex"
|
||||
- "100BASE-T Full-Duplex"
|
6
dts/bindings/ethernet/phy.yaml
Normal file
6
dts/bindings/ethernet/phy.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Copyright (c) 2021 IP-Logix Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Common fields for PHY devices
|
||||
|
||||
include: base.yaml
|
231
include/net/phy.h
Normal file
231
include/net/phy.h
Normal file
|
@ -0,0 +1,231 @@
|
|||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Public APIs for Ethernet PHY drivers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021 IP-Logix Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_PHY_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_PHY_H_
|
||||
|
||||
/**
|
||||
* @brief Ethernet PHY Interface
|
||||
* @defgroup ethernet_phy Ethernet PHY Interface
|
||||
* @ingroup networking
|
||||
* @{
|
||||
*/
|
||||
#include <zephyr/types.h>
|
||||
#include <device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief Ethernet link speeds. */
|
||||
enum phy_link_speed {
|
||||
/** 10Base-T Half-Duplex */
|
||||
LINK_HALF_10BASE_T = BIT(0),
|
||||
/** 10Base-T Full-Duplex */
|
||||
LINK_FULL_10BASE_T = BIT(1),
|
||||
/** 100Base-T Half-Duplex */
|
||||
LINK_HALF_100BASE_T = BIT(2),
|
||||
/** 100Base-T Full-Duplex */
|
||||
LINK_FULL_100BASE_T = BIT(3),
|
||||
};
|
||||
|
||||
#define PHY_LINK_IS_FULL_DUPLEX(x) (x & (BIT(1) | BIT(3)))
|
||||
#define PHY_LINK_IS_SPEED_100M(x) (x & (BIT(2) | BIT(3)))
|
||||
|
||||
/** @brief Link state */
|
||||
struct phy_link_state {
|
||||
/** Link speed */
|
||||
enum phy_link_speed speed;
|
||||
/** When true the link is active and connected */
|
||||
bool is_up;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef phy_callback_t
|
||||
* @brief Define the callback function signature for
|
||||
* `phy_link_callback_set()` function.
|
||||
*
|
||||
* @param dev PHY device structure
|
||||
* @param state Pointer to link_state structure.
|
||||
* @param user_data Pointer to data specified by user
|
||||
*/
|
||||
typedef void (*phy_callback_t)(const struct device *dev,
|
||||
struct phy_link_state *state,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* @cond INTERNAL_HIDDEN
|
||||
*
|
||||
* These are for internal use only, so skip these in
|
||||
* public documentation.
|
||||
*/
|
||||
__subsystem struct ethphy_driver_api {
|
||||
/** Get link state */
|
||||
int (*get_link)(const struct device *dev,
|
||||
struct phy_link_state *state);
|
||||
|
||||
/** Configure link */
|
||||
int (*cfg_link)(const struct device *dev,
|
||||
enum phy_link_speed adv_speeds);
|
||||
|
||||
/** Set callback to be invoked when link state changes. */
|
||||
int (*link_cb_set)(const struct device *dev, phy_callback_t cb,
|
||||
void *user_data);
|
||||
|
||||
/** Read PHY register */
|
||||
int (*read)(const struct device *dev, uint16_t reg_addr,
|
||||
uint32_t *data);
|
||||
|
||||
/** Write PHY register */
|
||||
int (*write)(const struct device *dev, uint16_t reg_addr,
|
||||
uint32_t data);
|
||||
};
|
||||
/**
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Configure PHY link
|
||||
*
|
||||
* This route configures the advertised link speeds.
|
||||
*
|
||||
* @param[in] dev PHY device structure
|
||||
* @param speeds OR'd link speeds to be advertised by the PHY
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -EIO If communication with PHY failed.
|
||||
* @retval -ENOTSUP If not supported.
|
||||
*/
|
||||
__syscall int phy_configure_link(const struct device *dev,
|
||||
enum phy_link_speed speeds);
|
||||
|
||||
static inline int z_impl_phy_configure_link(const struct device *dev,
|
||||
enum phy_link_speed speeds)
|
||||
{
|
||||
const struct ethphy_driver_api *api =
|
||||
(const struct ethphy_driver_api *)dev->api;
|
||||
|
||||
return api->cfg_link(dev, speeds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get PHY link state
|
||||
*
|
||||
* Returns the current state of the PHY link. This can be used by
|
||||
* to determine when a link is up and the negotiated link speed.
|
||||
*
|
||||
*
|
||||
* @param[in] dev PHY device structure
|
||||
* @param state Pointer to receive PHY state
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -EIO If communication with PHY failed.
|
||||
*/
|
||||
__syscall int phy_get_link_state(const struct device *dev,
|
||||
struct phy_link_state *state);
|
||||
|
||||
static inline int z_impl_phy_get_link_state(const struct device *dev,
|
||||
struct phy_link_state *state)
|
||||
{
|
||||
const struct ethphy_driver_api *api =
|
||||
(const struct ethphy_driver_api *)dev->api;
|
||||
|
||||
return api->get_link(dev, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set link state change callback
|
||||
*
|
||||
* Sets a callback that is invoked when link state changes. This is the
|
||||
* preferred method for ethernet drivers to be notified of the PHY link
|
||||
* state change.
|
||||
*
|
||||
* @param[in] dev PHY device structure
|
||||
* @param callback Callback handler
|
||||
* @param user_data Pointer to data specified by user.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -ENOTSUP If not supported.
|
||||
*/
|
||||
__syscall int phy_link_callback_set(const struct device *dev,
|
||||
phy_callback_t callback,
|
||||
void *user_data);
|
||||
|
||||
static inline int z_impl_phy_link_callback_set(const struct device *dev,
|
||||
phy_callback_t callback,
|
||||
void *user_data)
|
||||
{
|
||||
const struct ethphy_driver_api *api =
|
||||
(const struct ethphy_driver_api *)dev->api;
|
||||
|
||||
return api->link_cb_set(dev, callback, user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read PHY registers
|
||||
*
|
||||
* This routine provides a generic interface to read from a PHY register.
|
||||
*
|
||||
* @param[in] dev PHY device structure
|
||||
* @param[in] reg_addr Register address
|
||||
* @param value Pointer to receive read value
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -EIO If communication with PHY failed.
|
||||
*/
|
||||
__syscall int phy_read(const struct device *dev, uint16_t reg_addr,
|
||||
uint32_t *value);
|
||||
|
||||
static inline int z_impl_phy_read(const struct device *dev, uint16_t reg_addr,
|
||||
uint32_t *value)
|
||||
{
|
||||
const struct ethphy_driver_api *api =
|
||||
(const struct ethphy_driver_api *)dev->api;
|
||||
|
||||
return api->read(dev, reg_addr, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write PHY register
|
||||
*
|
||||
* This routine provides a generic interface to write to a PHY register.
|
||||
*
|
||||
* @param[in] dev PHY device structure
|
||||
* @param[in] reg_addr Register address
|
||||
* @param[in] value Value to write
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -EIO If communication with PHY failed.
|
||||
*/
|
||||
__syscall int phy_write(const struct device *dev, uint16_t reg_addr,
|
||||
uint32_t value);
|
||||
|
||||
static inline int z_impl_phy_write(const struct device *dev, uint16_t reg_addr,
|
||||
uint32_t value)
|
||||
{
|
||||
const struct ethphy_driver_api *api =
|
||||
(const struct ethphy_driver_api *)dev->api;
|
||||
|
||||
return api->write(dev, reg_addr, value);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <syscalls/phy.h>
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_PHY_H_ */
|
Loading…
Reference in a new issue