/* * Copyright 2023-2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_enet_mdio #include #include #include #include #include #include #include #include struct nxp_enet_mdio_config { const struct pinctrl_dev_config *pincfg; const struct device *module_dev; const struct device *clock_dev; clock_control_subsys_t clock_subsys; uint32_t mdc_freq; bool disable_preamble; }; struct nxp_enet_mdio_data { ENET_Type *base; struct k_mutex mdio_mutex; struct k_sem mdio_sem; bool interrupt_up; }; /* * This function is used for both read and write operations * in order to wait for the completion of an MDIO transaction. * It returns -ETIMEDOUT if timeout occurs as specified in DT, * otherwise returns 0 if EIR MII bit is set indicting completed * operation, otherwise -EIO. */ static int nxp_enet_mdio_wait_xfer(const struct device *dev) { struct nxp_enet_mdio_data *data = dev->data; ENET_Type *base = data->base; /* This function will not make sense from IRQ context */ if (k_is_in_isr()) { return -EWOULDBLOCK; } if (data->interrupt_up) { /* Enable the interrupt */ base->EIMR |= ENET_EIMR_MII_MASK; } else { /* If the interrupt is not available to use yet, just busy wait */ k_busy_wait(CONFIG_MDIO_NXP_ENET_TIMEOUT); k_sem_give(&data->mdio_sem); } /* Wait for the MDIO transaction to finish or time out */ k_sem_take(&data->mdio_sem, K_USEC(CONFIG_MDIO_NXP_ENET_TIMEOUT)); return 0; } /* MDIO Read API implementation */ static int nxp_enet_mdio_read(const struct device *dev, uint8_t prtad, uint8_t regad, uint16_t *read_data) { struct nxp_enet_mdio_data *data = dev->data; int ret; /* Only one MDIO bus operation attempt at a time */ (void)k_mutex_lock(&data->mdio_mutex, K_FOREVER); /* * Clear the bit (W1C) that indicates MDIO transfer is ready to * prepare to wait for it to be set once this read is done */ data->base->EIR |= ENET_EIR_MII_MASK; /* * Write MDIO frame to MII management register which will * send the read command and data out to the MDIO bus as this frame: * ST = start, 1 means start * OP = operation, 2 means read * PA = PHY/Port address * RA = Register/Device Address * TA = Turnaround, must be 2 to be valid * data = data to be written to the PHY register */ data->base->MMFR = ENET_MMFR_ST(0x1U) | ENET_MMFR_OP(MDIO_OP_C22_READ) | ENET_MMFR_PA(prtad) | ENET_MMFR_RA(regad) | ENET_MMFR_TA(0x2U); ret = nxp_enet_mdio_wait_xfer(dev); if (ret) { (void)k_mutex_unlock(&data->mdio_mutex); return ret; } /* The data is received in the same register that we wrote the command to */ *read_data = (data->base->MMFR & ENET_MMFR_DATA_MASK) >> ENET_MMFR_DATA_SHIFT; /* Clear the same bit as before because the event has been handled */ data->base->EIR |= ENET_EIR_MII_MASK; /* This MDIO interaction is finished */ (void)k_mutex_unlock(&data->mdio_mutex); return ret; } /* MDIO Write API implementation */ static int nxp_enet_mdio_write(const struct device *dev, uint8_t prtad, uint8_t regad, uint16_t write_data) { struct nxp_enet_mdio_data *data = dev->data; int ret; /* Only one MDIO bus operation attempt at a time */ (void)k_mutex_lock(&data->mdio_mutex, K_FOREVER); /* * Clear the bit (W1C) that indicates MDIO transfer is ready to * prepare to wait for it to be set once this write is done */ data->base->EIR |= ENET_EIR_MII_MASK; /* * Write MDIO frame to MII management register which will * send the write command and data out to the MDIO bus as this frame: * ST = start, 1 means start * OP = operation, 1 means write * PA = PHY/Port address * RA = Register/Device Address * TA = Turnaround, must be 2 to be valid * data = data to be written to the PHY register */ data->base->MMFR = ENET_MMFR_ST(0x1U) | ENET_MMFR_OP(MDIO_OP_C22_WRITE) | ENET_MMFR_PA(prtad) | ENET_MMFR_RA(regad) | ENET_MMFR_TA(0x2U) | write_data; ret = nxp_enet_mdio_wait_xfer(dev); if (ret) { (void)k_mutex_unlock(&data->mdio_mutex); return ret; } /* Clear the same bit as before because the event has been handled */ data->base->EIR |= ENET_EIR_MII_MASK; /* This MDIO interaction is finished */ (void)k_mutex_unlock(&data->mdio_mutex); return ret; } static const struct mdio_driver_api nxp_enet_mdio_api = { .read = nxp_enet_mdio_read, .write = nxp_enet_mdio_write, }; static void nxp_enet_mdio_isr_cb(const struct device *dev) { struct nxp_enet_mdio_data *data = dev->data; data->base->EIR |= ENET_EIR_MII_MASK; /* Signal that operation finished */ k_sem_give(&data->mdio_sem); /* Disable the interrupt */ data->base->EIMR &= ~ENET_EIMR_MII_MASK; } static void nxp_enet_mdio_post_module_reset_init(const struct device *dev) { const struct nxp_enet_mdio_config *config = dev->config; struct nxp_enet_mdio_data *data = dev->data; uint32_t enet_module_clock_rate; /* Set up MSCR register */ (void) clock_control_get_rate(config->clock_dev, config->clock_subsys, &enet_module_clock_rate); uint32_t mii_speed = (enet_module_clock_rate + 2 * config->mdc_freq - 1) / (2 * config->mdc_freq) - 1; uint32_t holdtime = (10 + NSEC_PER_SEC / enet_module_clock_rate - 1) / (NSEC_PER_SEC / enet_module_clock_rate) - 1; uint32_t mscr = ENET_MSCR_MII_SPEED(mii_speed) | ENET_MSCR_HOLDTIME(holdtime) | (config->disable_preamble ? ENET_MSCR_DIS_PRE_MASK : 0); data->base->MSCR = mscr; } void nxp_enet_mdio_callback(const struct device *dev, enum nxp_enet_callback_reason event, void *cb_data) { struct nxp_enet_mdio_data *data = dev->data; ARG_UNUSED(cb_data); switch (event) { case NXP_ENET_MODULE_RESET: nxp_enet_mdio_post_module_reset_init(dev); break; case NXP_ENET_INTERRUPT: nxp_enet_mdio_isr_cb(dev); break; case NXP_ENET_INTERRUPT_ENABLED: data->interrupt_up = true; break; default: break; } } static int nxp_enet_mdio_init(const struct device *dev) { const struct nxp_enet_mdio_config *config = dev->config; struct nxp_enet_mdio_data *data = dev->data; int ret = 0; data->base = (ENET_Type *)DEVICE_MMIO_GET(config->module_dev); ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); if (ret) { return ret; } ret = k_mutex_init(&data->mdio_mutex); if (ret) { return ret; } ret = k_sem_init(&data->mdio_sem, 0, 1); if (ret) { return ret; } /* All operations done after module reset should be done during device init too */ nxp_enet_mdio_post_module_reset_init(dev); return ret; } #define NXP_ENET_MDIO_INIT(inst) \ PINCTRL_DT_INST_DEFINE(inst); \ \ static const struct nxp_enet_mdio_config nxp_enet_mdio_cfg_##inst = { \ .module_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ .clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(inst))), \ .clock_subsys = (void *) DT_CLOCKS_CELL_BY_IDX( \ DT_INST_PARENT(inst), 0, name), \ .disable_preamble = DT_INST_PROP(inst, suppress_preamble), \ .mdc_freq = DT_INST_PROP(inst, clock_frequency), \ }; \ \ static struct nxp_enet_mdio_data nxp_enet_mdio_data_##inst; \ \ DEVICE_DT_INST_DEFINE(inst, &nxp_enet_mdio_init, NULL, \ &nxp_enet_mdio_data_##inst, &nxp_enet_mdio_cfg_##inst, \ POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, \ &nxp_enet_mdio_api); DT_INST_FOREACH_STATUS_OKAY(NXP_ENET_MDIO_INIT)