/* * Copyright (c) 2022 Grant Ramsay * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT espressif_esp32_mdio #include #include #include #include #include #include #include #include #include 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)