drivers: ethernet: vsc8541: add basic support for phy
add basic support for the microchip vsc8541 model phy. as first starter, 1000MBit/s mode is implemented. Signed-off-by: Sven Ginka <s.ginka@sensry.de>
This commit is contained in:
parent
f6faecd008
commit
e50645468c
4 changed files with 622 additions and 0 deletions
|
|
@ -5,6 +5,7 @@ zephyr_library_sources_ifdef(CONFIG_PHY_ADIN2111 phy_adin2111.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_PHY_DM8806 phy_dm8806.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PHY_TJA1103 phy_tja1103.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_KSZ8081 phy_microchip_ksz8081.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_VSC8541 phy_microchip_vsc8541.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PHY_TI_DP83825 phy_ti_dp83825.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PHY_REALTEK_RTL8211F phy_realtek_rtl8211f.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PHY_QUALCOMM_AR8031 phy_qualcomm_ar8031.c)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,15 @@ config PHY_MICROCHIP_KSZ8081
|
|||
help
|
||||
Enable Microchip KSZ8081 Ethernet PHY Driver
|
||||
|
||||
config PHY_MICROCHIP_VSC8541
|
||||
bool "Microchip VSC8541 PHY Driver"
|
||||
default y
|
||||
depends on DT_HAS_MICROCHIP_VSC8541_ENABLED
|
||||
depends on MDIO
|
||||
depends on GPIO
|
||||
help
|
||||
Enable Microchip VSC8541 Ethernet PHY Driver
|
||||
|
||||
config PHY_TI_DP83825
|
||||
bool "TI DP83825 PHY Driver"
|
||||
default y
|
||||
|
|
@ -98,4 +107,10 @@ config PHY_OA_TC14_PLCA_LIB
|
|||
help
|
||||
Enable Open Alliance TC14 PLCA generic lib.
|
||||
|
||||
config PHY_VERIFY_DEVICE_IDENTIFICATION
|
||||
bool "Verify selected phy to actual reported phy chip id"
|
||||
help
|
||||
Verify the organizationally unique identifier that is reported
|
||||
by the phy chip.
|
||||
|
||||
endif # "Ethernet PHY Drivers"
|
||||
|
|
|
|||
576
drivers/ethernet/phy/phy_microchip_vsc8541.c
Normal file
576
drivers/ethernet/phy/phy_microchip_vsc8541.c
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Copyright (c) 2024 sensry.io
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT microchip_vsc8541
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(microchip_vsc8541, CONFIG_PHY_LOG_LEVEL);
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/net/phy.h>
|
||||
#include <zephyr/net/mii.h>
|
||||
#include <zephyr/drivers/mdio.h>
|
||||
#include <string.h>
|
||||
#include <zephyr/sys/util_macro.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
/* phy page selectors */
|
||||
#define PHY_PAGE_0 0x00 /* main registers space active */
|
||||
#define PHY_PAGE_1 0x01 /* reg 16 - 30 will be redirected to ext. register space 1 */
|
||||
#define PHY_PAGE_2 0x02 /* reg 16 - 30 will be redirected to ext. register space 2 */
|
||||
#define PHY_PAGE_3 0x03 /* reg 0 - 30 will be redirected to gpio register space */
|
||||
|
||||
/* virtual register, treat higher byte as page selector and lower byte is register */
|
||||
#define PHY_REG(page, reg) ((page << 8) | (reg << 0))
|
||||
|
||||
/* Generic Register */
|
||||
#define PHY_REG_PAGE0_BMCR PHY_REG(PHY_PAGE_0, 0x00)
|
||||
#define PHY_REG_PAGE0_BMSR PHY_REG(PHY_PAGE_0, 0x01)
|
||||
#define PHY_REG_PAGE0_ID1 PHY_REG(PHY_PAGE_0, 0x02)
|
||||
#define PHY_REG_PAGE0_ID2 PHY_REG(PHY_PAGE_0, 0x03)
|
||||
#define PHY_REG_PAGE0_ADV PHY_REG(PHY_PAGE_0, 0x04)
|
||||
#define PHY_REG_LPA 0x05
|
||||
#define PHY_REG_EXP 0x06
|
||||
#define PHY_REG_PAGE0_CTRL1000 PHY_REG(PHY_PAGE_0, 0x09)
|
||||
#define PHY_REG_PAGE0_STAT1000 PHY_REG(0, 0x0A)
|
||||
#define PHY_REG_MMD_CTRL 0x0D
|
||||
#define PHY_REG_MMD_DATA 0x0E
|
||||
#define PHY_REG_STAT1000_EXT1 0x0F
|
||||
#define PHY_REG_PAGE0_STAT100 PHY_REG(PHY_PAGE_0, 0x10)
|
||||
#define PHY_REG_PAGE0_STAT1000_EXT2 PHY_REG(PHY_PAGE_0, 0x11)
|
||||
#define PHY_REG_AUX_CTRL 0x12
|
||||
#define PHY_REG_PAGE0_ERROR_COUNTER_1 PHY_REG(0, 0x13)
|
||||
#define PHY_REG_PAGE0_ERROR_COUNTER_2 PHY_REG(0, 0x14)
|
||||
#define PHY_REG_PAGE0_EXT_CTRL_STAT PHY_REG(PHY_PAGE_0, 0x16)
|
||||
#define PHY_REG_PAGE0_EXT_CONTROL_1 PHY_REG(PHY_PAGE_0, 0x17)
|
||||
#define PHY_REG_LED_MODE 0x1d
|
||||
|
||||
#define PHY_REG_PAGE_SELECTOR 0x1F
|
||||
|
||||
/* Extended Register */
|
||||
#define PHY_REG_PAGE1_EXT_MODE_CTRL PHY_REG(PHY_PAGE_1, 0x13)
|
||||
#define PHY_REG_PAGE2_RGMII_CONTROL PHY_REG(PHY_PAGE_2, 0x14)
|
||||
#define PHY_REG_PAGE2_MAC_IF_CONTROL PHY_REG(PHY_PAGE_2, 0x1b)
|
||||
|
||||
/* selected bits in registers */
|
||||
#define BMCR_RESET (1 << 15)
|
||||
#define BMCR_LOOPBACK (1 << 14)
|
||||
#define BMCR_ANENABLE (1 << 12)
|
||||
#define BMCR_ANRESTART (1 << 9)
|
||||
#define BMCR_FULLDPLX (1 << 8)
|
||||
#define BMCR_SPEED10 ((0 << 13) | (0 << 6))
|
||||
#define BMCR_SPEED100 ((1 << 13) | (0 << 6))
|
||||
#define BMCR_SPEED1000 ((0 << 13) | (1 << 6))
|
||||
|
||||
#define BMCR_SPEEDMASK ((1 << 13) | (1 << 6))
|
||||
|
||||
#define BMSR_LSTATUS (1 << 2)
|
||||
|
||||
enum vsc8541_interface {
|
||||
VSC8541_MII,
|
||||
VSC8541_RMII,
|
||||
VSC8541_GMII,
|
||||
VSC8541_RGMII,
|
||||
};
|
||||
|
||||
/* Thread stack size */
|
||||
#define STACK_SIZE 512
|
||||
|
||||
/* Thread priority */
|
||||
#define THREAD_PRIORITY 7
|
||||
|
||||
struct mc_vsc8541_config {
|
||||
uint8_t addr;
|
||||
const struct device *mdio_dev;
|
||||
enum vsc8541_interface microchip_interface_type;
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
|
||||
const struct gpio_dt_spec reset_gpio;
|
||||
#endif
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
|
||||
const struct gpio_dt_spec interrupt_gpio;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mc_vsc8541_data {
|
||||
const struct device *dev;
|
||||
|
||||
struct phy_link_state state;
|
||||
int active_page;
|
||||
|
||||
struct k_mutex mutex;
|
||||
|
||||
phy_callback_t cb;
|
||||
void *cb_data;
|
||||
|
||||
struct k_thread link_monitor_thread;
|
||||
uint8_t link_monitor_thread_stack[STACK_SIZE];
|
||||
};
|
||||
|
||||
static int phy_mc_vsc8541_read(const struct device *dev, uint16_t reg_addr, uint32_t *data);
|
||||
static int phy_mc_vsc8541_write(const struct device *dev, uint16_t reg_addr, uint32_t data);
|
||||
static void phy_mc_vsc8541_link_monitor(void *arg1, void *arg2, void *arg3);
|
||||
|
||||
#if CONFIG_PHY_VERIFY_DEVICE_IDENTIFICATION
|
||||
/**
|
||||
* @brief Reads the phy manufacture id and compares to designed, known model version
|
||||
*/
|
||||
static int phy_mc_vsc8541_verify_phy_id(const struct device *dev)
|
||||
{
|
||||
uint16_t phy_id_1;
|
||||
uint16_t phy_id_2;
|
||||
|
||||
if (0 != phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_ID1, (uint32_t *)&phy_id_1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (0 != phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_ID2, (uint32_t *)&phy_id_2)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (phy_id_1 == 0x0007) {
|
||||
if (phy_id_2 == 0x0771) {
|
||||
LOG_INF("model vsc8541-01 rev b");
|
||||
return 0;
|
||||
}
|
||||
if (phy_id_2 == 0x0772) {
|
||||
LOG_INF("model vsc8541-02/-05 rev c");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INF("phy id is %#.4x - %#.4x", phy_id_1, phy_id_2);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Low level reset procedure that toggles the reset gpio
|
||||
*/
|
||||
static int phy_mc_vsc8541_reset(const struct device *dev)
|
||||
{
|
||||
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
|
||||
const struct mc_vsc8541_config *cfg = dev->config;
|
||||
|
||||
if (!cfg->reset_gpio.port) {
|
||||
LOG_WRN("missing reset port definition");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* configure the reset pin */
|
||||
int ret = gpio_pin_configure_dt(&cfg->reset_gpio, GPIO_OUTPUT_ACTIVE);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
/* Start reset */
|
||||
ret = gpio_pin_set_dt(&cfg->reset_gpio, 0);
|
||||
if (ret) {
|
||||
LOG_WRN("failed to set reset gpio");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Wait as specified by datasheet */
|
||||
k_sleep(K_MSEC(200));
|
||||
|
||||
/* Reset over */
|
||||
gpio_pin_set_dt(&cfg->reset_gpio, 1);
|
||||
|
||||
/* After de-asserting reset, must wait before using the config interface */
|
||||
k_sleep(K_MSEC(200));
|
||||
}
|
||||
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios) */
|
||||
|
||||
/* According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1,
|
||||
* a PHY reset may take up to 0.5 s.
|
||||
*/
|
||||
k_sleep(K_MSEC(500));
|
||||
|
||||
#if CONFIG_PHY_VERIFY_DEVICE_IDENTIFICATION
|
||||
/* confirm phy organizationally unique identifier, if enabled */
|
||||
if (0 != phy_mc_vsc8541_verify_phy_id(dev)) {
|
||||
LOG_ERR("failed to verify phy id");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* set RGMII mode (must be executed BEFORE software reset -- see datasheet) */
|
||||
if (cfg->microchip_interface_type == VSC8541_RGMII) {
|
||||
ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_EXT_CONTROL_1,
|
||||
(0x0 << 13) | (0x2 << 11));
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* software reset */
|
||||
ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_BMCR, MII_BMCR_RESET);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* wait for phy finished software reset */
|
||||
uint32_t reg = 0;
|
||||
|
||||
do {
|
||||
phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_BMCR, ®);
|
||||
} while (reg & BMCR_RESET);
|
||||
|
||||
/* forced MDI-X */
|
||||
ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE1_EXT_MODE_CTRL, (3 << 2));
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* configure the RGMII clk delay */
|
||||
/* this is highly hardware dependent and may vary between pcb designs */
|
||||
reg = 0x0;
|
||||
/* RX_CLK delay */
|
||||
reg |= (0x5 << 4);
|
||||
/* TX_CLK delay */
|
||||
reg |= (0x5 << 0);
|
||||
ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE2_RGMII_CONTROL, reg);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* we use limited advertising, to force gigabit speed */
|
||||
/* initial version of this driver supports only 1GB/s */
|
||||
|
||||
/* 1000MBit/s + AUTO */
|
||||
ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_ADV, (1 << 8) | (1 << 6) | 0x01);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_CTRL1000, (1 << 12) | (1 << 11) | (1 << 9));
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* start auto negotiation */
|
||||
ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the phy state with current speed given by readings of the phy
|
||||
*
|
||||
* @param dev device structure of the phy
|
||||
* @param state being updated (we only update speed here)
|
||||
*/
|
||||
static int phy_mc_vsc8541_get_speed(const struct device *dev, struct phy_link_state *state)
|
||||
{
|
||||
int ret;
|
||||
uint32_t status;
|
||||
uint32_t link10_status;
|
||||
uint32_t link100_status;
|
||||
uint32_t link1000_status;
|
||||
|
||||
ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_BMSR, &status);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_EXT_CTRL_STAT, &link10_status);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_STAT100, &link100_status);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_STAT1000_EXT2, &link1000_status);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((status & (1 << 2)) == 0) {
|
||||
/* no link */
|
||||
state->speed = LINK_HALF_10BASE_T;
|
||||
}
|
||||
|
||||
if ((status & (1 << 5)) == 0) {
|
||||
/* auto negotiation not yet complete */
|
||||
state->speed = LINK_HALF_10BASE_T;
|
||||
}
|
||||
|
||||
if ((link1000_status & (1 << 12))) {
|
||||
state->speed = LINK_FULL_1000BASE_T;
|
||||
}
|
||||
if (link100_status & (1 << 12)) {
|
||||
state->speed = LINK_FULL_100BASE_T;
|
||||
}
|
||||
if (link10_status & (1 << 6)) {
|
||||
state->speed = LINK_FULL_10BASE_T;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the phy and starts the link monitor
|
||||
*
|
||||
*/
|
||||
static int phy_mc_vsc8541_init(const struct device *dev)
|
||||
{
|
||||
struct mc_vsc8541_data *data = dev->data;
|
||||
|
||||
data->cb = NULL;
|
||||
data->cb_data = NULL;
|
||||
data->state.is_up = false;
|
||||
data->state.speed = LINK_HALF_10BASE_T;
|
||||
data->active_page = -1;
|
||||
|
||||
/* Reset PHY */
|
||||
int ret = phy_mc_vsc8541_reset(dev);
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("initialize failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* setup thread to watch link state */
|
||||
k_thread_create(&data->link_monitor_thread,
|
||||
(k_thread_stack_t *)data->link_monitor_thread_stack, STACK_SIZE,
|
||||
phy_mc_vsc8541_link_monitor, (void *)dev, NULL, NULL, THREAD_PRIORITY, 0,
|
||||
K_NO_WAIT);
|
||||
|
||||
k_thread_name_set(&data->link_monitor_thread, "phy-link-mon");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the link status with readings from phy
|
||||
*
|
||||
* @param dev device structure to phy
|
||||
* @param state which is being updated
|
||||
*/
|
||||
static int phy_mc_vsc8541_get_link(const struct device *dev, struct phy_link_state *state)
|
||||
{
|
||||
int ret;
|
||||
uint32_t reg_sr;
|
||||
uint32_t reg_cr;
|
||||
|
||||
ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_BMSR, ®_sr);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_BMCR, ®_cr);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t hasLink = reg_sr & (1 << 2) ? 1 : 0;
|
||||
|
||||
uint32_t auto_negotiation_finished;
|
||||
|
||||
if (reg_cr & (BMCR_ANENABLE)) {
|
||||
/* auto negotiation active; update status */
|
||||
auto_negotiation_finished = reg_sr & (1 << 5) ? 1 : 0;
|
||||
} else {
|
||||
auto_negotiation_finished = 1;
|
||||
}
|
||||
|
||||
if (hasLink & auto_negotiation_finished) {
|
||||
state->is_up = 1;
|
||||
ret = phy_mc_vsc8541_get_speed(dev, state);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
state->is_up = 0;
|
||||
state->speed = LINK_HALF_10BASE_T;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the link speed; currently unused
|
||||
*
|
||||
*/
|
||||
static int phy_mc_vsc8541_cfg_link(const struct device *dev, enum phy_link_speed speeds)
|
||||
{
|
||||
/* the initial version does not support reconfiguration */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set callback which is used to announce link status changes
|
||||
*
|
||||
* @param dev device structure to phy device
|
||||
* @param cb
|
||||
* @param user_data
|
||||
*/
|
||||
static int phy_mc_vsc8541_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data)
|
||||
{
|
||||
struct mc_vsc8541_data *data = dev->data;
|
||||
|
||||
data->cb = cb;
|
||||
data->cb_data = user_data;
|
||||
|
||||
phy_mc_vsc8541_get_link(dev, &data->state);
|
||||
|
||||
data->cb(dev, &data->state, data->cb_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Monitor thread to check the link state and announce if changed
|
||||
*
|
||||
* @param arg1 provides a pointer to device structure
|
||||
* @param arg2 not used
|
||||
* @param arg3 not used
|
||||
*/
|
||||
void phy_mc_vsc8541_link_monitor(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
const struct device *dev = arg1;
|
||||
struct mc_vsc8541_data *data = dev->data;
|
||||
const struct mc_vsc8541_config *cfg = dev->config;
|
||||
|
||||
struct phy_link_state new_state;
|
||||
|
||||
while (1) {
|
||||
k_sleep(K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
|
||||
phy_mc_vsc8541_get_link(dev, &new_state);
|
||||
|
||||
if ((new_state.is_up != data->state.is_up) ||
|
||||
(new_state.speed != data->state.speed)) {
|
||||
/* state changed */
|
||||
data->state.is_up = new_state.is_up;
|
||||
data->state.speed = new_state.speed;
|
||||
|
||||
if (data->cb) {
|
||||
/* announce new state */
|
||||
data->cb(dev, &data->state, data->cb_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reading of phy register content at given address via mdio interface
|
||||
*
|
||||
* - high byte of register address defines page
|
||||
* - low byte of register address defines the address within selected page
|
||||
* - to speed up, we store the last used page and only swap page if needed
|
||||
*
|
||||
*/
|
||||
static int phy_mc_vsc8541_read(const struct device *dev, uint16_t reg_addr, uint32_t *data)
|
||||
{
|
||||
const struct mc_vsc8541_config *cfg = dev->config;
|
||||
struct mc_vsc8541_data *dev_data = dev->data;
|
||||
int ret;
|
||||
|
||||
*data = 0U;
|
||||
|
||||
/* decode page */
|
||||
uint32_t page = reg_addr >> 8;
|
||||
|
||||
/* mask out lower byte */
|
||||
reg_addr &= 0x00ff;
|
||||
|
||||
/* select page, given by register upper byte */
|
||||
if (dev_data->active_page != page) {
|
||||
ret = mdio_write(cfg->mdio_dev, cfg->addr, PHY_REG_PAGE_SELECTOR, (uint16_t)page);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
dev_data->active_page = (int)page;
|
||||
}
|
||||
|
||||
/* select register, given by register lower byte */
|
||||
ret = mdio_read(cfg->mdio_dev, cfg->addr, reg_addr, (uint16_t *)data);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writing of new value to phy register at given address via mdio interface
|
||||
*
|
||||
* - high byte of register address defines page
|
||||
* - low byte of register address defines the address within selected page
|
||||
* - to speed up, we store the last used page and only swap page if needed
|
||||
*
|
||||
*/
|
||||
static int phy_mc_vsc8541_write(const struct device *dev, uint16_t reg_addr, uint32_t data)
|
||||
{
|
||||
const struct mc_vsc8541_config *cfg = dev->config;
|
||||
struct mc_vsc8541_data *dev_data = dev->data;
|
||||
int ret;
|
||||
|
||||
/* decode page */
|
||||
uint32_t page = reg_addr >> 8;
|
||||
|
||||
/* mask out lower byte */
|
||||
reg_addr &= 0x00ff;
|
||||
|
||||
/* select page, given by register upper byte */
|
||||
if (dev_data->active_page != page) {
|
||||
ret = mdio_write(cfg->mdio_dev, cfg->addr, PHY_REG_PAGE_SELECTOR, (uint16_t)page);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
dev_data->active_page = (int)page;
|
||||
}
|
||||
|
||||
/* write register, given by lower byte */
|
||||
ret = mdio_write(cfg->mdio_dev, cfg->addr, reg_addr, (uint16_t)data);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEVICE_API(ethphy, mc_vsc8541_phy_api) = {
|
||||
.get_link = phy_mc_vsc8541_get_link,
|
||||
.cfg_link = phy_mc_vsc8541_cfg_link,
|
||||
.link_cb_set = phy_mc_vsc8541_link_cb_set,
|
||||
.read = phy_mc_vsc8541_read,
|
||||
.write = phy_mc_vsc8541_write,
|
||||
};
|
||||
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
|
||||
#define RESET_GPIO(n) .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}),
|
||||
#else
|
||||
#define RESET_GPIO(n)
|
||||
#endif /* reset gpio */
|
||||
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
|
||||
#define INTERRUPT_GPIO(n) .interrupt_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}),
|
||||
#else
|
||||
#define INTERRUPT_GPIO(n)
|
||||
#endif /* interrupt gpio */
|
||||
|
||||
#define MICROCHIP_VSC8541_INIT(n) \
|
||||
static const struct mc_vsc8541_config mc_vsc8541_##n##_config = { \
|
||||
.addr = DT_INST_REG_ADDR(n), \
|
||||
.mdio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)), \
|
||||
.microchip_interface_type = DT_INST_ENUM_IDX(n, microchip_interface_type), \
|
||||
RESET_GPIO(n) INTERRUPT_GPIO(n)}; \
|
||||
\
|
||||
static struct mc_vsc8541_data mc_vsc8541_##n##_data; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &phy_mc_vsc8541_init, NULL, &mc_vsc8541_##n##_data, \
|
||||
&mc_vsc8541_##n##_config, POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, \
|
||||
&mc_vsc8541_phy_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(MICROCHIP_VSC8541_INIT)
|
||||
30
dts/bindings/ethernet/microchip,vsc8541-phy.yaml
Normal file
30
dts/bindings/ethernet/microchip,vsc8541-phy.yaml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Copyright (c) 2024 sensry.io
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Single Port Gigabit Ethernet Copper PHY with GMII/RGMII/MII/RMII Interfaces
|
||||
|
||||
compatible: "microchip,vsc8541"
|
||||
|
||||
include: [ethernet-phy.yaml]
|
||||
|
||||
on-bus: mdio
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
description: |
|
||||
5-bit MDIO PHY address for the attached PHY.
|
||||
|
||||
reset-gpios:
|
||||
type: phandle-array
|
||||
description: GPIO connected to MAC PHY reset signal pin. Reset is active low.
|
||||
|
||||
microchip,interface-type:
|
||||
type: string
|
||||
required: true
|
||||
description: Which type of phy connection the phy is set up for
|
||||
enum:
|
||||
- "mii"
|
||||
- "rmii"
|
||||
- "gmii"
|
||||
- "rgmii"
|
||||
Loading…
Reference in a new issue