zephyr/drivers/mdio/mdio_adin2111.c
Georgij Cernysiov 943bc1cebc 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>
2023-05-26 09:50:09 -04:00

208 lines
4.7 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/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)