Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | /*
* 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)
|