drivers: mdio: add adin2111

Adds MDIO driver. Works via exposed
ADIN2111 functions.

It is possible to access Clause 45 and 22 registers.

Due to MDIO API limitation Clause 45 access
is done using driver specific MDIO functions.

Provides API and functions for PHY driver.

Signed-off-by: Georgij Cernysiov <geo.cgv@gmail.com>
This commit is contained in:
Georgij Cernysiov 2023-05-12 14:59:03 +02:00 committed by Anas Nashif
parent 9a15d72b32
commit 943bc1cebc
7 changed files with 300 additions and 0 deletions

View file

@ -288,6 +288,7 @@
/drivers/ethernet/*adin2111* @GeorgeCGV
/drivers/ethernet/phy/ @rlubos @tbursztyka @arvinf
/drivers/mdio/ @rlubos @tbursztyka @arvinf
/drivers/mdio/*adin2111* @GeorgeCGV
/drivers/flash/ @nashif @de-nordic
/drivers/flash/*stm32_qspi* @lmajewski
/drivers/flash/*b91* @andy-liu-telink

View file

@ -6,3 +6,4 @@ zephyr_library_sources_ifdef(CONFIG_MDIO_SHELL mdio_shell.c)
zephyr_library_sources_ifdef(CONFIG_MDIO_ATMEL_SAM mdio_sam.c)
zephyr_library_sources_ifdef(CONFIG_MDIO_ESP32 mdio_esp32.c)
zephyr_library_sources_ifdef(CONFIG_MDIO_NXP_S32_NETC mdio_nxp_s32_netc.c)
zephyr_library_sources_ifdef(CONFIG_MDIO_ADIN2111 mdio_adin2111.c)

View file

@ -28,6 +28,7 @@ config MDIO_SHELL
source "drivers/mdio/Kconfig.esp32"
source "drivers/mdio/Kconfig.sam"
source "drivers/mdio/Kconfig.nxp_s32"
source "drivers/mdio/Kconfig.adin2111"
config MDIO_INIT_PRIORITY
int "Init priority"

View file

@ -0,0 +1,10 @@
# Copyright 2023 PHOENIX CONTACT Electronics GmbH
# SPDX-License-Identifier: Apache-2.0
config MDIO_ADIN2111
bool "NXP S32 NETC External MDIO driver"
default y
depends on DT_HAS_ADI_ADIN2111_MDIO_ENABLED
depends on ETH_ADIN2111
help
Enable ADIN2111 MDIO driver.

View file

@ -0,0 +1,207 @@
/*
* Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(mdio_adin2111, CONFIG_MDIO_LOG_LEVEL);
#define DT_DRV_COMPAT adi_adin2111_mdio
#include <stdint.h>
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/mdio.h>
#include <zephyr/drivers/mdio/mdio_adin2111.h>
#include <zephyr/drivers/ethernet/eth_adin2111.h>
/* MDIO ready check retry delay */
#define ADIN2111_MDIO_READY_AWAIT_DELAY_POLL_US 5U
/* Number of retries for MDIO ready check */
#define ADIN2111_MDIO_READY_AWAIT_RETRY_COUNT 10U
/* MDIO Access Register 1 */
#define ADIN2111_MDIOACC0 0x20U
/* MDIO Access Register 2 */
#define ADIN2111_MDIOACC1 0x21U
/* MDIO MDIOACC Transaction Done */
#define ADIN211_MDIOACC_MDIO_TRDONE BIT(31)
struct mdio_adin2111_config {
const struct device *adin;
};
static int mdio_adin2111_wait_ready(const struct device *dev, uint16_t reg,
uint32_t *out)
{
const struct mdio_adin2111_config *const cfg = dev->config;
uint32_t count;
int ret;
for (count = 0U; count < ADIN2111_MDIO_READY_AWAIT_RETRY_COUNT; ++count) {
ret = eth_adin2111_reg_read(cfg->adin, reg, out);
if (ret >= 0) {
if ((*out) & ADIN211_MDIOACC_MDIO_TRDONE) {
break;
}
ret = -ETIMEDOUT;
}
k_sleep(K_USEC(ADIN2111_MDIO_READY_AWAIT_DELAY_POLL_US));
}
return ret;
}
int adin2111_mdio_c45_read(const struct device *dev, uint8_t prtad,
uint8_t devad, uint16_t regad,
uint16_t *data)
{
const struct mdio_adin2111_config *const cfg = dev->config;
uint32_t rdy;
uint32_t cmd;
int ret;
/* address op */
cmd = (prtad & 0x1FU) << 21;
cmd |= (devad & 0x1FU) << 16;
cmd |= regad;
ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd);
if (ret < 0) {
return ret;
}
/* read op */
cmd = (cmd & ~UINT16_MAX) | (0x3U << 26);
ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC1, cmd);
if (ret < 0) {
return ret;
}
ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC1, &rdy);
if (ret < 0) {
return ret;
}
/* read out */
ret = eth_adin2111_reg_read(cfg->adin, ADIN2111_MDIOACC1, &cmd);
*data = cmd & UINT16_MAX;
return ret;
}
int adin2111_mdio_c45_write(const struct device *dev, uint8_t prtad,
uint8_t devad, uint16_t regad,
uint16_t data)
{
const struct mdio_adin2111_config *const cfg = dev->config;
uint32_t rdy;
uint32_t cmd;
int ret;
/* address op */
cmd = (prtad & 0x1FU) << 21;
cmd |= (devad & 0x1FU) << 16;
cmd |= regad;
ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd);
if (ret < 0) {
return ret;
}
/* write op */
cmd |= BIT(26);
cmd = (cmd & ~UINT16_MAX) | data;
ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC1, cmd);
if (ret < 0) {
return ret;
}
ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC1, &rdy);
return ret;
}
static int mdio_adin2111_read(const struct device *dev, uint8_t prtad,
uint8_t devad, uint16_t *data)
{
const struct mdio_adin2111_config *const cfg = dev->config;
uint32_t read;
uint32_t cmd;
int ret;
cmd = BIT(28);
cmd |= 0x3U << 26;
cmd |= (prtad & 0x1FU) << 21;
cmd |= (devad & 0x1FU) << 16;
ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd);
if (ret >= 0) {
ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC0, &read);
*data = read & UINT16_MAX;
}
return ret;
}
static int mdio_adin2111_write(const struct device *dev, uint8_t prtad,
uint8_t devad, uint16_t data)
{
const struct mdio_adin2111_config *const cfg = dev->config;
uint32_t cmd;
uint32_t rdy;
int ret;
cmd = BIT(28);
cmd |= BIT(26);
cmd |= (prtad & 0x1FU) << 21;
cmd |= (devad & 0x1FU) << 16;
cmd |= data;
ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd);
if (ret >= 0) {
ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC0, &rdy);
}
return ret;
}
static void mdio_adin2111_bus_enable(const struct device *dev)
{
const struct mdio_adin2111_config *const cfg = dev->config;
eth_adin2111_lock(cfg->adin, K_FOREVER);
}
static void mdio_adin2111_bus_disable(const struct device *dev)
{
const struct mdio_adin2111_config *const cfg = dev->config;
eth_adin2111_unlock(cfg->adin);
}
static const struct mdio_driver_api mdio_adin2111_api = {
.read = mdio_adin2111_read,
.write = mdio_adin2111_write,
.bus_enable = mdio_adin2111_bus_enable,
.bus_disable = mdio_adin2111_bus_disable
};
#define ADIN2111_MDIO_INIT(n) \
static const struct mdio_adin2111_config mdio_adin2111_config_##n = { \
.adin = DEVICE_DT_GET(DT_INST_BUS(n)), \
}; \
DEVICE_DT_INST_DEFINE(n, NULL, NULL, \
NULL, &mdio_adin2111_config_##n, \
POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, \
&mdio_adin2111_api);
DT_INST_FOREACH_STATUS_OKAY(ADIN2111_MDIO_INIT)

View file

@ -0,0 +1,19 @@
# Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH
# SPDX-License-Identifier: Apache-2.0
description: ADIN2111 MDIO Driver node
compatible: "adi,adin2111-mdio"
include: mdio-controller.yaml
on-bus: adin2111
properties:
"#address-cells":
required: true
const: 1
"#size-cells":
required: true
const: 0

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_MDIO_ADIN2111_H__
#define ZEPHYR_INCLUDE_DRIVERS_MDIO_ADIN2111_H__
#include <stdint.h>
#include <zephyr/device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Read from MDIO Bus using Clause 45 access
*
* @note The caller is responsible for device lock.
* Shall not be called from ISR.
*
* @param[in] dev MDIO device.
* @param[in] prtad Port address.
* @param[in] devad Device address.
* @param[in] regad Register address.
* @param[out] data Pointer to receive read data.
*
* @retval 0 If successful.
* @retval -EIO General input / output error.
* @retval -ETIMEDOUT If transaction timedout on the bus.
* @retval <0 Error, a negative errno code.
*/
int adin2111_mdio_c45_read(const struct device *dev, uint8_t prtad,
uint8_t devad, uint16_t regad, uint16_t *data);
/**
* @brief Write to MDIO bus using Clause 45 access
*
* @note The caller is responsible for device lock.
* Shall not be called from ISR.
*
* @param[in] dev MDIO device.
* @param[in] prtad Port address.
* @param[in] devad Device address.
* @param[in] regad Register address.
* @param[in] data Data to write.
*
* @retval 0 If successful.
* @retval -EIO General input / output error.
* @retval -ETIMEDOUT If transaction timedout on the bus.
* @retval <0 Error, a negative errno code.
*/
int adin2111_mdio_c45_write(const struct device *dev, uint8_t prtad,
uint8_t devad, uint16_t regad, uint16_t data);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_MDIO_ADIN2111_H__ */