280ddaef4a
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>
159 lines
3.7 KiB
C
159 lines
3.7 KiB
C
/*
|
|
* Copyright (c) 2022 Grant Ramsay <grant.ramsay@hotmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT espressif_esp32_mdio
|
|
|
|
#include <soc.h>
|
|
#include <zephyr/drivers/clock_control.h>
|
|
#include <zephyr/drivers/mdio.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
|
|
#include <esp_mac.h>
|
|
#include <hal/emac_hal.h>
|
|
#include <hal/emac_ll.h>
|
|
|
|
LOG_MODULE_REGISTER(mdio_esp32, CONFIG_MDIO_LOG_LEVEL);
|
|
|
|
#define PHY_OPERATION_TIMEOUT_US 1000
|
|
|
|
struct mdio_esp32_dev_data {
|
|
struct k_sem sem;
|
|
emac_hal_context_t hal;
|
|
};
|
|
|
|
struct mdio_esp32_dev_config {
|
|
const struct pinctrl_dev_config *pcfg;
|
|
};
|
|
|
|
static int mdio_transfer(const struct device *dev, uint8_t prtad, uint8_t regad,
|
|
bool write, uint16_t data_in, uint16_t *data_out)
|
|
{
|
|
struct mdio_esp32_dev_data *const dev_data = dev->data;
|
|
|
|
k_sem_take(&dev_data->sem, K_FOREVER);
|
|
|
|
if (emac_ll_is_mii_busy(dev_data->hal.mac_regs)) {
|
|
LOG_ERR("phy busy");
|
|
k_sem_give(&dev_data->sem);
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (write) {
|
|
emac_ll_set_phy_data(dev_data->hal.mac_regs, data_in);
|
|
}
|
|
emac_hal_set_phy_cmd(&dev_data->hal, prtad, regad, write);
|
|
|
|
/* Poll until operation complete */
|
|
bool success = false;
|
|
|
|
for (uint32_t t_us = 0; t_us < PHY_OPERATION_TIMEOUT_US; t_us += 100) {
|
|
k_sleep(K_USEC(100));
|
|
if (!emac_ll_is_mii_busy(dev_data->hal.mac_regs)) {
|
|
success = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!success) {
|
|
LOG_ERR("phy timeout");
|
|
k_sem_give(&dev_data->sem);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
if (!write && data_out != NULL) {
|
|
*data_out = emac_ll_get_phy_data(dev_data->hal.mac_regs);
|
|
}
|
|
|
|
k_sem_give(&dev_data->sem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdio_esp32_read(const struct device *dev, uint8_t prtad, uint8_t regad,
|
|
uint16_t *data)
|
|
{
|
|
return mdio_transfer(dev, prtad, regad, false, 0, data);
|
|
|
|
}
|
|
|
|
static int mdio_esp32_write(const struct device *dev, uint8_t prtad,
|
|
uint8_t regad, uint16_t data)
|
|
{
|
|
return mdio_transfer(dev, prtad, regad, true, data, NULL);
|
|
}
|
|
|
|
static void mdio_esp32_bus_enable(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
}
|
|
|
|
static void mdio_esp32_bus_disable(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
}
|
|
|
|
static int mdio_esp32_initialize(const struct device *dev)
|
|
{
|
|
const struct mdio_esp32_dev_config *const cfg = dev->config;
|
|
struct mdio_esp32_dev_data *const dev_data = dev->data;
|
|
int res;
|
|
|
|
k_sem_init(&dev_data->sem, 1, 1);
|
|
|
|
res = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
|
|
if (res != 0) {
|
|
goto err;
|
|
}
|
|
|
|
const struct device *clock_dev =
|
|
DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_NODELABEL(mdio)));
|
|
clock_control_subsys_t clock_subsys =
|
|
(clock_control_subsys_t)DT_CLOCKS_CELL(DT_NODELABEL(mdio), offset);
|
|
|
|
res = clock_control_on(clock_dev, clock_subsys);
|
|
if (res != 0) {
|
|
goto err;
|
|
}
|
|
|
|
/* Only the mac registers are required for MDIO */
|
|
dev_data->hal.mac_regs = &EMAC_MAC;
|
|
|
|
/* Init MDIO clock */
|
|
emac_hal_set_csr_clock_range(&dev_data->hal, esp_clk_apb_freq());
|
|
|
|
return 0;
|
|
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
static const struct mdio_driver_api mdio_esp32_driver_api = {
|
|
.read = mdio_esp32_read,
|
|
.write = mdio_esp32_write,
|
|
.bus_enable = mdio_esp32_bus_enable,
|
|
.bus_disable = mdio_esp32_bus_disable,
|
|
};
|
|
|
|
#define MDIO_ESP32_CONFIG(n) \
|
|
static const struct mdio_esp32_dev_config mdio_esp32_dev_config_##n = { \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
|
};
|
|
|
|
#define MDIO_ESP32_DEVICE(n) \
|
|
PINCTRL_DT_INST_DEFINE(n); \
|
|
MDIO_ESP32_CONFIG(n); \
|
|
static struct mdio_esp32_dev_data mdio_esp32_dev_data##n; \
|
|
DEVICE_DT_INST_DEFINE(n, \
|
|
&mdio_esp32_initialize, \
|
|
NULL, \
|
|
&mdio_esp32_dev_data##n, \
|
|
&mdio_esp32_dev_config_##n, POST_KERNEL, \
|
|
CONFIG_MDIO_INIT_PRIORITY, \
|
|
&mdio_esp32_driver_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(MDIO_ESP32_DEVICE)
|