zephyr/drivers/mdio/mdio_adin2111.c
Manuel Argüelles 280ddaef4a mdio: introduce Clause 45 APIs
Add `mdio_read_c45()`/`mdio_write_c45()` APIs for Clause 45 access
and remove the `protocol` MDIO binding property so that MDIO bus
controller can support more than one protocol.

A new MDIO header is introduced with generic opcodes, MMD and
registers addresses, to be used by MDIO and PHY drivers.

Existing MDIO drivers that support both Clause 22 and Clause 45
access are migrated to the new APIs.

Signed-off-by: Manuel Argüelles <manuel.arguelles@nxp.com>
2023-09-28 09:33:10 +02:00

209 lines
4.8 KiB
C

/*
* 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/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;
}
static int mdio_adin2111_read_c45(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;
}
static int mdio_adin2111_write_c45(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 regad, 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 |= (regad & 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 regad, 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 |= (regad & 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,
.read_c45 = mdio_adin2111_read_c45,
.write_c45 = mdio_adin2111_write_c45,
.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)