/* * Copyright 2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_enet_qos_mdio #include #include #include #include #include #include #include LOG_MODULE_REGISTER(mdio_nxp_enet_qos, CONFIG_MDIO_LOG_LEVEL); struct nxp_enet_qos_mdio_config { const struct device *enet_dev; }; struct nxp_enet_qos_mdio_data { struct k_mutex mdio_mutex; }; struct mdio_transaction { enum mdio_opcode op; union { uint16_t write_data; uint16_t *read_data; }; uint8_t portaddr; uint8_t regaddr; enet_qos_t *base; struct k_mutex *mdio_bus_mutex; }; static bool check_busy(enet_qos_t *base) { uint32_t val = base->MAC_MDIO_ADDRESS; /* Return the busy bit */ return ENET_QOS_REG_GET(MAC_MDIO_ADDRESS, GB, val); } static int do_transaction(struct mdio_transaction *mdio) { enet_qos_t *base = mdio->base; uint8_t goc_1_code; int ret; k_mutex_lock(mdio->mdio_bus_mutex, K_FOREVER); if (mdio->op == MDIO_OP_C22_WRITE) { base->MAC_MDIO_DATA = /* Prepare the data to be written */ ENET_QOS_REG_PREP(MAC_MDIO_DATA, GD, mdio->write_data); goc_1_code = 0b0; } else if (mdio->op == MDIO_OP_C22_READ) { goc_1_code = 0b1; } else { ret = -EINVAL; goto done; } base->MAC_MDIO_ADDRESS = /* OP command */ ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, GOC_1, goc_1_code) | ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, GOC_0, 0b1) | /* PHY address */ ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, PA, mdio->portaddr) | /* Register address */ ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, RDA, mdio->regaddr); base->MAC_MDIO_ADDRESS = /* Start the transaction */ ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, GB, 0b1); ret = -ETIMEDOUT; for (int i = CONFIG_MDIO_NXP_ENET_QOS_RECHECK_COUNT; i > 0; i--) { if (!check_busy(base)) { ret = 0; break; } k_busy_wait(CONFIG_MDIO_NXP_ENET_QOS_RECHECK_TIME); } if (ret) { LOG_ERR("MDIO transaction timed out"); goto done; } if (mdio->op == MDIO_OP_C22_READ) { uint32_t val = mdio->base->MAC_MDIO_DATA; *mdio->read_data = /* Decipher the read data */ ENET_QOS_REG_GET(MAC_MDIO_DATA, GD, val); } done: k_mutex_unlock(mdio->mdio_bus_mutex); return ret; } static int nxp_enet_qos_mdio_read(const struct device *dev, uint8_t portaddr, uint8_t regaddr, uint16_t *read_data) { const struct nxp_enet_qos_mdio_config *config = dev->config; struct nxp_enet_qos_mdio_data *data = dev->data; enet_qos_t *base = ENET_QOS_MODULE_CFG(config->enet_dev)->base; struct mdio_transaction mdio_read = { .op = MDIO_OP_C22_READ, .read_data = read_data, .portaddr = portaddr, .regaddr = regaddr, .base = base, .mdio_bus_mutex = &data->mdio_mutex, }; return do_transaction(&mdio_read); } static int nxp_enet_qos_mdio_write(const struct device *dev, uint8_t portaddr, uint8_t regaddr, uint16_t write_data) { const struct nxp_enet_qos_mdio_config *config = dev->config; struct nxp_enet_qos_mdio_data *data = dev->data; enet_qos_t *base = ENET_QOS_MODULE_CFG(config->enet_dev)->base; struct mdio_transaction mdio_write = { .op = MDIO_OP_C22_WRITE, .write_data = write_data, .portaddr = portaddr, .regaddr = regaddr, .base = base, .mdio_bus_mutex = &data->mdio_mutex, }; return do_transaction(&mdio_write); } static const struct mdio_driver_api nxp_enet_qos_mdio_api = { .read = nxp_enet_qos_mdio_read, .write = nxp_enet_qos_mdio_write, }; static int nxp_enet_qos_mdio_init(const struct device *dev) { const struct nxp_enet_qos_mdio_config *mdio_config = dev->config; struct nxp_enet_qos_mdio_data *data = dev->data; const struct nxp_enet_qos_config *config = ENET_QOS_MODULE_CFG(mdio_config->enet_dev); uint32_t enet_module_clk_rate; int ret, divider; ret = k_mutex_init(&data->mdio_mutex); if (ret) { return ret; } ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &enet_module_clk_rate); if (ret) { return ret; } enet_module_clk_rate /= 1000000; if (enet_module_clk_rate >= 20 && enet_module_clk_rate < 35) { divider = 2; } else if (enet_module_clk_rate < 60) { divider = 3; } else if (enet_module_clk_rate < 100) { divider = 0; } else if (enet_module_clk_rate < 150) { divider = 1; } else if (enet_module_clk_rate < 250) { divider = 4; } else { LOG_ERR("ENET QOS clk rate does not allow MDIO"); return -ENOTSUP; } config->base->MAC_MDIO_ADDRESS = /* Configure the MDIO clock range / divider */ ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, CR, divider); return 0; } #define NXP_ENET_QOS_MDIO_INIT(inst) \ \ static const struct nxp_enet_qos_mdio_config \ nxp_enet_qos_mdio_cfg_##inst = { \ .enet_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ }; \ \ static struct nxp_enet_qos_mdio_data nxp_enet_qos_mdio_data_##inst; \ \ DEVICE_DT_INST_DEFINE(inst, &nxp_enet_qos_mdio_init, NULL, \ &nxp_enet_qos_mdio_data_##inst, \ &nxp_enet_qos_mdio_cfg_##inst, \ POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, \ &nxp_enet_qos_mdio_api); \ DT_INST_FOREACH_STATUS_OKAY(NXP_ENET_QOS_MDIO_INIT)