diff --git a/drivers/stepper/adi_tmc/CMakeLists.txt b/drivers/stepper/adi_tmc/CMakeLists.txt index dbf4121447d9c..4363d09edb1c5 100644 --- a/drivers/stepper/adi_tmc/CMakeLists.txt +++ b/drivers/stepper/adi_tmc/CMakeLists.txt @@ -5,7 +5,9 @@ zephyr_library() zephyr_library_property(ALLOW_EMPTY TRUE) zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC2209 tmc22xx.c) -zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC50XX tmc50xx.c) -add_subdirectory_ifdef(CONFIG_STEPPER_ADI_TMC51XX tmc51xx) + +if(CONFIG_STEPPER_ADI_TMC51XX OR CONFIG_STEPPER_ADI_TMC50XX) + add_subdirectory(tmc5xxx) +endif() add_subdirectory_ifdef(CONFIG_STEPPER_ADI_TMC bus) diff --git a/drivers/stepper/adi_tmc/Kconfig.tmc50xx b/drivers/stepper/adi_tmc/Kconfig.tmc50xx index eeb17293456a5..6f7a8d84cedeb 100644 --- a/drivers/stepper/adi_tmc/Kconfig.tmc50xx +++ b/drivers/stepper/adi_tmc/Kconfig.tmc50xx @@ -5,7 +5,7 @@ config STEPPER_ADI_TMC50XX bool "Activate trinamic tmc50xx stepper driver" depends on DT_HAS_ADI_TMC50XX_ENABLED && STEPPER_ADI_TMC - select STEPPER_ADI_TMC_SPI + select STEPPER_ADI_TMC_SPI if $(dt_compat_on_bus,$(DT_COMPAT_ADI_TMC50XX),spi) default y module = TMC50XX diff --git a/drivers/stepper/adi_tmc/adi_tmc5xxx_common.h b/drivers/stepper/adi_tmc/adi_tmc5xxx_common.h deleted file mode 100644 index 7eafe62326eb8..0000000000000 --- a/drivers/stepper/adi_tmc/adi_tmc5xxx_common.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @file drivers/stepper/adi_tmc/adi_tmc5xxx_common.h - * - * @brief Common TMC5xxx stepper controller driver definitions - */ - -/** - * SPDX-FileCopyrightText: Copyright (c) 2024 Jilay Sandeep Pandya - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_ADI_TMC5XXX_COMMON_H_ -#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_ADI_TMC5XXX_COMMON_H_ - -#include "adi_tmc_reg.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @name TMC5xxx module functions - * @anchor TMC5XXX_FUNCTIONS - * - * @{ - */ - -/** - * @brief Calculate the velocity in full clock cycles from the velocity in Hz - * - * @param velocity_hz Velocity in Hz - * @param clock_frequency Clock frequency in Hz - * - * @return Calculated velocity in full clock cycles - */ -static inline uint32_t tmc5xxx_calculate_velocity_from_hz_to_fclk(uint64_t velocity_hz, - uint32_t clock_frequency) -{ - __ASSERT_NO_MSG(clock_frequency); - return (velocity_hz << TMC5XXX_CLOCK_FREQ_SHIFT) / clock_frequency; -} - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_ADI_TMC5XXX_COMMON_H_ */ diff --git a/drivers/stepper/adi_tmc/tmc50xx.c b/drivers/stepper/adi_tmc/tmc50xx.c deleted file mode 100644 index 1e83e64b67ae4..0000000000000 --- a/drivers/stepper/adi_tmc/tmc50xx.c +++ /dev/null @@ -1,760 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG - * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya - * SPDX-License-Identifier: Apache-2.0 - */ - -#define DT_DRV_COMPAT adi_tmc50xx - -#include - -#include -#include - -#include -#include "adi_tmc5xxx_common.h" - -#include -LOG_MODULE_REGISTER(tmc50xx, CONFIG_STEPPER_LOG_LEVEL); - -struct tmc50xx_data { - struct k_sem sem; -}; - -struct tmc50xx_config { - const uint32_t gconf; - struct spi_dt_spec spi; - const uint32_t clock_frequency; -}; - -struct tmc50xx_stepper_data { - struct k_work_delayable stallguard_dwork; - /* Work item to run the callback in a thread context. */ - struct k_work_delayable rampstat_callback_dwork; - /* device pointer required to access config in k_work */ - const struct device *stepper; - stepper_event_callback_t callback; - void *event_cb_user_data; -}; - -struct tmc50xx_stepper_config { - const uint8_t index; - const uint16_t default_micro_step_res; - const int8_t sg_threshold; - const bool is_sg_enabled; - const uint32_t sg_velocity_check_interval_ms; - const uint32_t sg_threshold_velocity; - /* parent controller required for bus communication */ - const struct device *controller; -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN - const struct tmc_ramp_generator_data default_ramp_config; -#endif -}; - -static int read_actual_position(const struct tmc50xx_stepper_config *config, int32_t *position); - -static int tmc50xx_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val) -{ - const struct tmc50xx_config *config = dev->config; - struct tmc50xx_data *data = dev->data; - const struct spi_dt_spec bus = config->spi; - int err; - - k_sem_take(&data->sem, K_FOREVER); - - err = tmc_spi_write_register(&bus, TMC5XXX_WRITE_BIT, reg_addr, reg_val); - - k_sem_give(&data->sem); - - if (err < 0) { - LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val); - return err; - } - return 0; -} - -static int tmc50xx_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) -{ - const struct tmc50xx_config *config = dev->config; - struct tmc50xx_data *data = dev->data; - const struct spi_dt_spec bus = config->spi; - int err; - - k_sem_take(&data->sem, K_FOREVER); - - err = tmc_spi_read_register(&bus, TMC5XXX_ADDRESS_MASK, reg_addr, reg_val); - - k_sem_give(&data->sem); - - if (err < 0) { - LOG_ERR("Failed to read register 0x%x", reg_addr); - return err; - } - return 0; -} - -static int tmc50xx_stepper_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, void *user_data) -{ - struct tmc50xx_stepper_data *data = dev->data; - - data->callback = callback; - data->event_cb_user_data = user_data; - return 0; -} - -static int read_vactual(const struct tmc50xx_stepper_config *config, int32_t *actual_velocity) -{ - __ASSERT(actual_velocity != NULL, "actual_velocity pointer must not be NULL"); - int err; - - err = tmc50xx_read(config->controller, TMC50XX_VACTUAL(config->index), actual_velocity); - if (err) { - LOG_ERR("Failed to read VACTUAL register"); - return err; - } - - *actual_velocity = sign_extend(*actual_velocity, TMC_RAMP_VACTUAL_SHIFT); - if (actual_velocity) { - LOG_DBG("actual velocity: %d", *actual_velocity); - } - return 0; -} - -static int stallguard_enable(const struct device *dev, const bool enable) -{ - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_SWMODE(config->index), ®_value); - if (err) { - LOG_ERR("Failed to read SWMODE register"); - return -EIO; - } - - if (enable) { - reg_value |= TMC5XXX_SW_MODE_SG_STOP_ENABLE; - - int32_t actual_velocity; - - err = read_vactual(config, &actual_velocity); - if (err) { - return -EIO; - } - if (abs(actual_velocity) < config->sg_threshold_velocity) { - return -EAGAIN; - } - } else { - reg_value &= ~TMC5XXX_SW_MODE_SG_STOP_ENABLE; - } - err = tmc50xx_write(config->controller, TMC50XX_SWMODE(config->index), reg_value); - if (err) { - LOG_ERR("Failed to write SWMODE register"); - return -EIO; - } - - LOG_DBG("Stallguard %s", enable ? "enabled" : "disabled"); - return 0; -} - -static void stallguard_work_handler(struct k_work *work) -{ - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct tmc50xx_stepper_data *stepper_data = - CONTAINER_OF(dwork, struct tmc50xx_stepper_data, stallguard_dwork); - int err; - const struct tmc50xx_stepper_config *stepper_config = stepper_data->stepper->config; - - err = stallguard_enable(stepper_data->stepper, true); - if (err == -EAGAIN) { - k_work_reschedule(dwork, K_MSEC(stepper_config->sg_velocity_check_interval_ms)); - } - if (err == -EIO) { - LOG_ERR("Failed to enable stallguard because of I/O error"); - return; - } -} - - -static void execute_callback(const struct device *dev, const enum stepper_event event) -{ - struct tmc50xx_stepper_data *data = dev->data; - - if (!data->callback) { - LOG_WRN_ONCE("No callback registered"); - return; - } - data->callback(dev, event, data->event_cb_user_data); -} - -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG - -static void log_stallguard(struct tmc50xx_stepper_data *stepper_data, const uint32_t drv_status) -{ - const struct tmc50xx_stepper_config *stepper_config = stepper_data->stepper->config; - int32_t position; - int err; - - err = read_actual_position(stepper_config, &position); - if (err != 0) { - LOG_ERR("%s: Failed to read XACTUAL register", stepper_data->stepper->name); - return; - } - - const uint8_t sg_result = FIELD_GET(TMC5XXX_DRV_STATUS_SG_RESULT_MASK, drv_status); - const bool sg_status = FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status); - - LOG_DBG("%s position: %d | sg result: %3d status: %d", - stepper_data->stepper->name, position, sg_result, sg_status); -} - -#endif - -static void rampstat_work_reschedule(struct k_work_delayable *rampstat_callback_dwork) -{ - k_work_reschedule(rampstat_callback_dwork, - K_MSEC(CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); -} - -static void rampstat_work_handler(struct k_work *work) -{ - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - - struct tmc50xx_stepper_data *stepper_data = - CONTAINER_OF(dwork, struct tmc50xx_stepper_data, rampstat_callback_dwork); - const struct tmc50xx_stepper_config *stepper_config = stepper_data->stepper->config; - - __ASSERT_NO_MSG(stepper_config->controller != NULL); - - uint32_t drv_status; - int err; - - err = tmc50xx_read(stepper_config->controller, TMC50XX_DRVSTATUS(stepper_config->index), - &drv_status); - if (err != 0) { - LOG_ERR("%s: Failed to read DRVSTATUS register", stepper_data->stepper->name); - return; - } -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG - log_stallguard(stepper_data, drv_status); -#endif - if (FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status) == 1U) { - LOG_INF("%s: Stall detected", stepper_data->stepper->name); - err = tmc50xx_write(stepper_config->controller, - TMC50XX_RAMPMODE(stepper_config->index), - TMC5XXX_RAMPMODE_HOLD_MODE); - if (err != 0) { - LOG_ERR("%s: Failed to stop motor", stepper_data->stepper->name); - return; - } - } - - uint32_t rampstat_value; - - err = tmc50xx_read(stepper_config->controller, TMC50XX_RAMPSTAT(stepper_config->index), - &rampstat_value); - if (err != 0) { - LOG_ERR("%s: Failed to read RAMPSTAT register", stepper_data->stepper->name); - return; - } - - const uint8_t ramp_stat_values = FIELD_GET(TMC5XXX_RAMPSTAT_INT_MASK, rampstat_value); - - if (ramp_stat_values > 0) { - switch (ramp_stat_values) { - - case TMC5XXX_STOP_LEFT_EVENT: - LOG_DBG("RAMPSTAT %s:Left end-stop detected", stepper_data->stepper->name); - execute_callback(stepper_data->stepper, - STEPPER_EVENT_LEFT_END_STOP_DETECTED); - break; - - case TMC5XXX_STOP_RIGHT_EVENT: - LOG_DBG("RAMPSTAT %s:Right end-stop detected", stepper_data->stepper->name); - execute_callback(stepper_data->stepper, - STEPPER_EVENT_RIGHT_END_STOP_DETECTED); - break; - - case TMC5XXX_POS_REACHED_EVENT: - case TMC5XXX_POS_REACHED: - case TMC5XXX_POS_REACHED_AND_EVENT: - LOG_DBG("RAMPSTAT %s:Position reached", stepper_data->stepper->name); - execute_callback(stepper_data->stepper, STEPPER_EVENT_STEPS_COMPLETED); - break; - - case TMC5XXX_STOP_SG_EVENT: - LOG_DBG("RAMPSTAT %s:Stall detected", stepper_data->stepper->name); - stallguard_enable(stepper_data->stepper, false); - execute_callback(stepper_data->stepper, STEPPER_EVENT_STALL_DETECTED); - break; - default: - LOG_ERR("Illegal ramp stat bit field"); - break; - } - } else { - rampstat_work_reschedule(&stepper_data->rampstat_callback_dwork); - } -} - -static int tmc50xx_stepper_enable(const struct device *dev) -{ - LOG_DBG("Enabling Stepper motor controller %s", dev->name); - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_CHOPCONF(config->index), ®_value); - if (err != 0) { - return -EIO; - } - - reg_value |= TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; - - return tmc50xx_write(config->controller, TMC50XX_CHOPCONF(config->index), reg_value); -} - -static int tmc50xx_stepper_disable(const struct device *dev) -{ - LOG_DBG("Disabling Stepper motor controller %s", dev->name); - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_CHOPCONF(config->index), ®_value); - if (err != 0) { - return -EIO; - } - - reg_value &= ~TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; - - return tmc50xx_write(config->controller, TMC50XX_CHOPCONF(config->index), reg_value); -} - -static int tmc50xx_stepper_is_moving(const struct device *dev, bool *is_moving) -{ - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_DRVSTATUS(config->index), ®_value); - - if (err != 0) { - LOG_ERR("%s: Failed to read DRVSTATUS register", dev->name); - return -EIO; - } - - *is_moving = (FIELD_GET(TMC5XXX_DRV_STATUS_STST_BIT, reg_value) != 1U); - LOG_DBG("Stepper motor controller %s is moving: %d", dev->name, *is_moving); - return 0; -} - -int tmc50xx_stepper_set_max_velocity(const struct device *dev, uint32_t velocity) -{ - const struct tmc50xx_stepper_config *config = dev->config; - const struct tmc50xx_config *tmc50xx_config = config->controller->config; - const uint32_t clock_frequency = tmc50xx_config->clock_frequency; - uint32_t velocity_fclk; - int err; - - velocity_fclk = tmc5xxx_calculate_velocity_from_hz_to_fclk(velocity, clock_frequency); - - err = tmc50xx_write(config->controller, TMC50XX_VMAX(config->index), velocity_fclk); - if (err != 0) { - LOG_ERR("%s: Failed to set max velocity", dev->name); - return -EIO; - } - return 0; -} - -static int tmc50xx_stepper_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution res) -{ - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_CHOPCONF(config->index), ®_value); - if (err != 0) { - return -EIO; - } - - reg_value &= ~TMC5XXX_CHOPCONF_MRES_MASK; - reg_value |= ((MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - LOG2(res)) - << TMC5XXX_CHOPCONF_MRES_SHIFT); - - err = tmc50xx_write(config->controller, TMC50XX_CHOPCONF(config->index), reg_value); - if (err != 0) { - return -EIO; - } - - LOG_DBG("Stepper motor controller %s set micro step resolution to 0x%x", dev->name, - reg_value); - return 0; -} - -static int tmc50xx_stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *res) -{ - const struct tmc50xx_stepper_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc50xx_read(config->controller, TMC50XX_CHOPCONF(config->index), ®_value); - if (err != 0) { - return -EIO; - } - reg_value &= TMC5XXX_CHOPCONF_MRES_MASK; - reg_value >>= TMC5XXX_CHOPCONF_MRES_SHIFT; - *res = (1 << (MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - reg_value)); - LOG_DBG("Stepper motor controller %s get micro step resolution: %d", dev->name, *res); - return 0; -} - -static int tmc50xx_stepper_set_reference_position(const struct device *dev, const int32_t position) -{ - const struct tmc50xx_stepper_config *config = dev->config; - int err; - - err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), - TMC5XXX_RAMPMODE_HOLD_MODE); - if (err != 0) { - return -EIO; - } - - err = tmc50xx_write(config->controller, TMC50XX_XACTUAL(config->index), position); - if (err != 0) { - return -EIO; - } - LOG_DBG("Stepper motor controller %s set actual position to %d", dev->name, position); - return 0; -} - -static int read_actual_position(const struct tmc50xx_stepper_config *config, int32_t *position) -{ - int err; - - err = tmc50xx_read(config->controller, TMC50XX_XACTUAL(config->index), position); - if (err != 0) { - return -EIO; - } - return 0; -} - -static int tmc50xx_stepper_get_actual_position(const struct device *dev, int32_t *position) -{ - const struct tmc50xx_stepper_config *config = dev->config; - int err; - - err = read_actual_position(config, position); - if (err != 0) { - return -EIO; - } - LOG_DBG("%s actual position: %d", dev->name, *position); - return 0; -} - -static int tmc50xx_stepper_move_to(const struct device *dev, const int32_t micro_steps) -{ - LOG_DBG("%s set target position to %d", dev->name, micro_steps); - const struct tmc50xx_stepper_config *config = dev->config; - struct tmc50xx_stepper_data *data = dev->data; - int err; - - if (config->is_sg_enabled) { - stallguard_enable(dev, false); - } - - err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), - TMC5XXX_RAMPMODE_POSITIONING_MODE); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_XTARGET(config->index), micro_steps); - if (err != 0) { - return -EIO; - } - - if (config->is_sg_enabled) { - k_work_reschedule(&data->stallguard_dwork, - K_MSEC(config->sg_velocity_check_interval_ms)); - } - if (data->callback) { - rampstat_work_reschedule(&data->rampstat_callback_dwork); - } - return 0; -} - -static int tmc50xx_stepper_move_by(const struct device *dev, const int32_t micro_steps) -{ - int err; - int32_t position; - - err = stepper_get_actual_position(dev, &position); - if (err != 0) { - return -EIO; - } - int32_t target_position = position + micro_steps; - - LOG_DBG("%s moved to %d by steps: %d", dev->name, target_position, micro_steps); - - return tmc50xx_stepper_move_to(dev, target_position); -} - -static int tmc50xx_stepper_run(const struct device *dev, const enum stepper_direction direction) -{ - LOG_DBG("Stepper motor controller %s run", dev->name); - const struct tmc50xx_stepper_config *config = dev->config; - struct tmc50xx_stepper_data *data = dev->data; - int err; - - if (config->is_sg_enabled) { - err = stallguard_enable(dev, false); - if (err != 0) { - return -EIO; - } - } - - switch (direction) { - case STEPPER_DIRECTION_POSITIVE: - err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), - TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); - if (err != 0) { - return -EIO; - } - break; - - case STEPPER_DIRECTION_NEGATIVE: - err = tmc50xx_write(config->controller, TMC50XX_RAMPMODE(config->index), - TMC5XXX_RAMPMODE_NEGATIVE_VELOCITY_MODE); - if (err != 0) { - return -EIO; - } - break; - } - - if (config->is_sg_enabled) { - k_work_reschedule(&data->stallguard_dwork, - K_MSEC(config->sg_velocity_check_interval_ms)); - } - if (data->callback) { - rampstat_work_reschedule(&data->rampstat_callback_dwork); - } - return 0; -} - -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN - -int tmc50xx_stepper_set_ramp(const struct device *dev, - const struct tmc_ramp_generator_data *ramp_data) -{ - LOG_DBG("Stepper motor controller %s set ramp", dev->name); - const struct tmc50xx_stepper_config *config = dev->config; - int err; - - err = tmc50xx_write(config->controller, TMC50XX_VSTART(config->index), ramp_data->vstart); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_A1(config->index), ramp_data->a1); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_AMAX(config->index), ramp_data->amax); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_D1(config->index), ramp_data->d1); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_DMAX(config->index), ramp_data->dmax); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_V1(config->index), ramp_data->v1); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_VMAX(config->index), ramp_data->vmax); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_VSTOP(config->index), ramp_data->vstop); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_TZEROWAIT(config->index), - ramp_data->tzerowait); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_VHIGH(config->index), ramp_data->vhigh); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_VCOOLTHRS(config->index), - ramp_data->vcoolthrs); - if (err != 0) { - return -EIO; - } - err = tmc50xx_write(config->controller, TMC50XX_IHOLD_IRUN(config->index), - ramp_data->iholdrun); - if (err != 0) { - return -EIO; - } - return 0; -} - -#endif - -static int tmc50xx_init(const struct device *dev) -{ - LOG_DBG("TMC50XX stepper motor controller %s initialized", dev->name); - struct tmc50xx_data *data = dev->data; - const struct tmc50xx_config *config = dev->config; - int err; - - k_sem_init(&data->sem, 1, 1); - - if (!spi_is_ready_dt(&config->spi)) { - LOG_ERR("SPI bus is not ready"); - return -ENODEV; - } - - /* Init non motor-index specific registers here. */ - LOG_DBG("GCONF: %d", config->gconf); - err = tmc50xx_write(dev, TMC5XXX_GCONF, config->gconf); - if (err != 0) { - return -EIO; - } - - /* Read GSTAT register values to clear any errors SPI Datagram. */ - uint32_t gstat_value; - - err = tmc50xx_read(dev, TMC5XXX_GSTAT, &gstat_value); - if (err != 0) { - return -EIO; - } - - LOG_DBG("Device %s initialized", dev->name); - return 0; -} - -static int tmc50xx_stepper_init(const struct device *dev) -{ - const struct tmc50xx_stepper_config *stepper_config = dev->config; - struct tmc50xx_stepper_data *data = dev->data; - int err; - - LOG_DBG("Controller: %s, Stepper: %s", stepper_config->controller->name, dev->name); - - if (stepper_config->is_sg_enabled) { - k_work_init_delayable(&data->stallguard_dwork, stallguard_work_handler); - - err = tmc50xx_write(stepper_config->controller, - TMC50XX_SWMODE(stepper_config->index), BIT(10)); - if (err != 0) { - return -EIO; - } - - LOG_DBG("Setting stall guard to %d with delay %d ms", stepper_config->sg_threshold, - stepper_config->sg_velocity_check_interval_ms); - if (!IN_RANGE(stepper_config->sg_threshold, TMC5XXX_SG_MIN_VALUE, - TMC5XXX_SG_MAX_VALUE)) { - LOG_ERR("Stallguard threshold out of range"); - return -EINVAL; - } - - int32_t stall_guard_threshold = (int32_t)stepper_config->sg_threshold; - - err = tmc50xx_write( - stepper_config->controller, TMC50XX_COOLCONF(stepper_config->index), - stall_guard_threshold << TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT); - if (err != 0) { - return -EIO; - } - k_work_reschedule(&data->stallguard_dwork, K_NO_WAIT); - } - -#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN - err = tmc50xx_stepper_set_ramp(dev, &stepper_config->default_ramp_config); - if (err != 0) { - return -EIO; - } -#endif - - k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); - rampstat_work_reschedule(&data->rampstat_callback_dwork); - err = tmc50xx_stepper_set_micro_step_res(dev, stepper_config->default_micro_step_res); - if (err != 0) { - return -EIO; - } - return 0; -} - -static DEVICE_API(stepper, tmc50xx_stepper_api) = { - .enable = tmc50xx_stepper_enable, - .disable = tmc50xx_stepper_disable, - .is_moving = tmc50xx_stepper_is_moving, - .move_by = tmc50xx_stepper_move_by, - .set_micro_step_res = tmc50xx_stepper_set_micro_step_res, - .get_micro_step_res = tmc50xx_stepper_get_micro_step_res, - .set_reference_position = tmc50xx_stepper_set_reference_position, - .get_actual_position = tmc50xx_stepper_get_actual_position, - .move_to = tmc50xx_stepper_move_to, - .run = tmc50xx_stepper_run, - .set_event_callback = tmc50xx_stepper_set_event_callback, -}; - -#define TMC50XX_SHAFT_CONFIG(child) \ - (DT_PROP(child, invert_direction) << TMC50XX_GCONF_SHAFT_SHIFT(DT_REG_ADDR(child))) | - -#define TMC50XX_STEPPER_CONFIG_DEFINE(child) \ - COND_CODE_1(DT_PROP_EXISTS(child, stallguard_threshold_velocity), \ - BUILD_ASSERT(DT_PROP(child, stallguard_threshold_velocity), \ - "stallguard threshold velocity must be a positive value"), ()); \ - IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(child))); \ - static const struct tmc50xx_stepper_config tmc50xx_stepper_config_##child = { \ - .controller = DEVICE_DT_GET(DT_PARENT(child)), \ - .default_micro_step_res = DT_PROP(child, micro_step_res), \ - .index = DT_REG_ADDR(child), \ - .sg_threshold = DT_PROP(child, stallguard2_threshold), \ - .sg_threshold_velocity = DT_PROP(child, stallguard_threshold_velocity), \ - .sg_velocity_check_interval_ms = DT_PROP(child, \ - stallguard_velocity_check_interval_ms), \ - .is_sg_enabled = DT_PROP(child, activate_stallguard2), \ - IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, \ - (.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC50XX(child))) }; - -#define TMC50XX_STEPPER_DATA_DEFINE(child) \ - static struct tmc50xx_stepper_data tmc50xx_stepper_data_##child = { \ - .stepper = DEVICE_DT_GET(child),}; - -#define TMC50XX_STEPPER_DEFINE(child) \ - DEVICE_DT_DEFINE(child, tmc50xx_stepper_init, NULL, &tmc50xx_stepper_data_##child, \ - &tmc50xx_stepper_config_##child, POST_KERNEL, \ - CONFIG_STEPPER_INIT_PRIORITY, &tmc50xx_stepper_api); - -#define TMC50XX_DEFINE(inst) \ - BUILD_ASSERT(DT_INST_CHILD_NUM(inst) <= 2, "tmc50xx can drive two steppers at max"); \ - BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) > 0), \ - "clock frequency must be non-zero positive value"); \ - static struct tmc50xx_data tmc50xx_data_##inst; \ - static const struct tmc50xx_config tmc50xx_config_##inst = { \ - .gconf = ( \ - (DT_INST_PROP(inst, poscmp_enable) << TMC50XX_GCONF_POSCMP_ENABLE_SHIFT) | \ - (DT_INST_PROP(inst, test_mode) << TMC50XX_GCONF_TEST_MODE_SHIFT) | \ - DT_INST_FOREACH_CHILD(inst, TMC50XX_SHAFT_CONFIG) \ - (DT_INST_PROP(inst, lock_gconf) << TMC50XX_LOCK_GCONF_SHIFT)), \ - .spi = SPI_DT_SPEC_INST_GET(inst, (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | \ - SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8)), 0), \ - .clock_frequency = DT_INST_PROP(inst, clock_frequency),}; \ - DT_INST_FOREACH_CHILD(inst, TMC50XX_STEPPER_CONFIG_DEFINE); \ - DT_INST_FOREACH_CHILD(inst, TMC50XX_STEPPER_DATA_DEFINE); \ - DT_INST_FOREACH_CHILD(inst, TMC50XX_STEPPER_DEFINE); \ - DEVICE_DT_INST_DEFINE(inst, tmc50xx_init, NULL, &tmc50xx_data_##inst, \ - &tmc50xx_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY,\ - NULL); - -DT_INST_FOREACH_STATUS_OKAY(TMC50XX_DEFINE) diff --git a/drivers/stepper/adi_tmc/tmc51xx/CMakeLists.txt b/drivers/stepper/adi_tmc/tmc51xx/CMakeLists.txt deleted file mode 100644 index 027a7be4b51fd..0000000000000 --- a/drivers/stepper/adi_tmc/tmc51xx/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty -# SPDX-License-Identifier: Apache-2.0 - -zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC51XX tmc51xx.c tmc51xx_spi.c tmc51xx_uart.c) diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.c b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.c deleted file mode 100644 index a8f2d983a9b57..0000000000000 --- a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.c +++ /dev/null @@ -1,847 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025 Prevas A/S - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include - -#include -#include "tmc51xx.h" -#include "../adi_tmc5xxx_common.h" - -#include -LOG_MODULE_REGISTER(tmc51xx, CONFIG_STEPPER_LOG_LEVEL); - -static inline int tmc51xx_bus_check(const struct device *dev) -{ - const struct tmc51xx_config *config = dev->config; - - return config->bus_io->check(&config->bus, config->comm_type); -} - -static int read_actual_position(const struct device *dev, int32_t *position); -static void rampstat_work_handler(struct k_work *work); -static void tmc51xx_diag0_gpio_callback_handler(const struct device *port, struct gpio_callback *cb, - gpio_port_pins_t pins); -static int rampstat_read_clear(const struct device *dev, uint32_t *rampstat_value); - -static int tmc51xx_write(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val) -{ - const struct tmc51xx_config *config = dev->config; - struct tmc51xx_data *data = dev->data; - int err; - - k_sem_take(&data->sem, K_FOREVER); - - err = config->bus_io->write(dev, reg_addr, reg_val); - - k_sem_give(&data->sem); - - if (err < 0) { - LOG_ERR("Failed to write register 0x%x with value 0x%x", reg_addr, reg_val); - return err; - } - return 0; -} - -static int tmc51xx_read(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) -{ - const struct tmc51xx_config *config = dev->config; - struct tmc51xx_data *data = dev->data; - int err; - - k_sem_take(&data->sem, K_FOREVER); - - err = config->bus_io->read(dev, reg_addr, reg_val); - - k_sem_give(&data->sem); - - if (err < 0) { - LOG_ERR("Failed to read register 0x%x", reg_addr); - return err; - } - return 0; -} - -static int tmc51xx_stepper_set_event_callback(const struct device *dev, - stepper_event_callback_t callback, void *user_data) -{ - struct tmc51xx_data *data = dev->data; - __maybe_unused const struct tmc51xx_config *config = dev->config; - - __maybe_unused int err; - - data->callback = callback; - data->event_cb_user_data = user_data; - - /* Configure DIAG0 GPIO interrupt pin */ - IF_ENABLED(TMC51XX_BUS_SPI, ({ - if ((config->comm_type == TMC_COMM_SPI) && config->diag0_gpio.port) { - LOG_INF("Configuring DIAG0 GPIO interrupt pin"); - if (!gpio_is_ready_dt(&config->diag0_gpio)) { - LOG_ERR("DIAG0 interrupt GPIO not ready"); - return -ENODEV; - } - - err = gpio_pin_configure_dt(&config->diag0_gpio, GPIO_INPUT); - if (err < 0) { - LOG_ERR("Could not configure DIAG0 GPIO (%d)", err); - return err; - } - k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); - - err = gpio_pin_interrupt_configure_dt(&config->diag0_gpio, GPIO_INT_EDGE_RISING); - if (err) { - LOG_ERR("failed to configure DIAG0 interrupt (err %d)", err); - return -EIO; - } - - /* Initialize and add GPIO callback */ - gpio_init_callback(&data->diag0_cb, tmc51xx_diag0_gpio_callback_handler, - BIT(config->diag0_gpio.pin)); - - err = gpio_add_callback(config->diag0_gpio.port, &data->diag0_cb); - if (err < 0) { - LOG_ERR("Could not add DIAG0 pin GPIO callback (%d)", err); - return -EIO; - } - - /* Clear any pending interrupts */ - uint32_t rampstat_value; - - err = rampstat_read_clear(dev, &rampstat_value); - if (err != 0) { - return -EIO; - } - }})) - - return 0; -} - -static int read_vactual(const struct device *dev, int32_t *actual_velocity) -{ - int err; - uint32_t raw_value; - - err = tmc51xx_read(dev, TMC51XX_VACTUAL, &raw_value); - if (err) { - LOG_ERR("Failed to read VACTUAL register"); - return err; - } - - *actual_velocity = sign_extend(raw_value, TMC_RAMP_VACTUAL_SHIFT); - if (*actual_velocity) { - LOG_DBG("actual velocity: %d", *actual_velocity); - } - return 0; -} - -static int stallguard_enable(const struct device *dev, const bool enable) -{ - const struct tmc51xx_config *config = dev->config; - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_SWMODE, ®_value); - if (err) { - LOG_ERR("Failed to read SWMODE register"); - return -EIO; - } - - if (enable) { - reg_value |= TMC5XXX_SW_MODE_SG_STOP_ENABLE; - - int32_t actual_velocity; - - err = read_vactual(dev, &actual_velocity); - if (err) { - return -EIO; - } - if (abs(actual_velocity) < config->sg_threshold_velocity) { - return -EAGAIN; - } - } else { - reg_value &= ~TMC5XXX_SW_MODE_SG_STOP_ENABLE; - } - err = tmc51xx_write(dev, TMC51XX_SWMODE, reg_value); - if (err) { - LOG_ERR("Failed to write SWMODE register"); - return -EIO; - } - - LOG_DBG("Stallguard %s", enable ? "enabled" : "disabled"); - return 0; -} - -static void stallguard_work_handler(struct k_work *work) -{ - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct tmc51xx_data const *stepper_data = - CONTAINER_OF(dwork, struct tmc51xx_data, stallguard_dwork); - const struct device *dev = stepper_data->stepper; - const struct tmc51xx_config *config = dev->config; - int err; - - err = stallguard_enable(dev, true); - if (err == -EAGAIN) { - k_work_reschedule(dwork, K_MSEC(config->sg_velocity_check_interval_ms)); - } - if (err == -EIO) { - LOG_ERR("Failed to enable stallguard because of I/O error"); - } -} - -static void stepper_trigger_callback(const struct device *dev, const enum stepper_event event) -{ - struct tmc51xx_data *data = dev->data; - - if (!data->callback) { - LOG_WRN_ONCE("No callback registered"); - return; - } - data->callback(dev, event, data->event_cb_user_data); -} - -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_STALLGUARD_LOG - -static void log_stallguard(const struct device *dev, const uint32_t drv_status) -{ - int32_t position; - int err; - - err = read_actual_position(dev, &position); - if (err != 0) { - LOG_ERR("%s: Failed to read XACTUAL register", dev->name); - return; - } - - const uint8_t sg_result = FIELD_GET(TMC5XXX_DRV_STATUS_SG_RESULT_MASK, drv_status); - const bool sg_status = FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status); - - LOG_DBG("%s position: %d | sg result: %3d status: %d", dev->name, position, sg_result, - sg_status); -} - -#endif /* CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_STALLGUARD_LOG */ - -static int rampstat_read_clear(const struct device *dev, uint32_t *rampstat_value) -{ - int err; - - err = tmc51xx_read(dev, TMC51XX_RAMPSTAT, rampstat_value); - if (err == 0) { - err = tmc51xx_write(dev, TMC51XX_RAMPSTAT, *rampstat_value); - } - return err; -} - -static void rampstat_work_handler(struct k_work *work) -{ - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - - struct tmc51xx_data *stepper_data = - CONTAINER_OF(dwork, struct tmc51xx_data, rampstat_callback_dwork); - const struct device *dev = stepper_data->stepper; - __maybe_unused const struct tmc51xx_config *config = dev->config; - - __ASSERT_NO_MSG(dev); - - uint32_t drv_status; - int err; - - err = tmc51xx_read(dev, TMC51XX_DRVSTATUS, &drv_status); - if (err != 0) { - LOG_ERR("%s: Failed to read DRVSTATUS register", dev->name); - return; - } -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_STALLGUARD_LOG - log_stallguard(dev, drv_status); -#endif - if (FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status) == 1U) { - LOG_INF("%s: Stall detected", dev->name); - err = tmc51xx_write(dev, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_HOLD_MODE); - if (err != 0) { - LOG_ERR("%s: Failed to stop motor", dev->name); - return; - } - } - - uint32_t rampstat_value; - - err = rampstat_read_clear(dev, &rampstat_value); - if (err != 0) { - LOG_ERR("%s: Failed to read RAMPSTAT register", dev->name); - return; - } - - const uint8_t ramp_stat_values = FIELD_GET(TMC5XXX_RAMPSTAT_INT_MASK, rampstat_value); - - if (ramp_stat_values > 0) { - switch (ramp_stat_values) { - case TMC5XXX_STOP_LEFT_EVENT: - LOG_DBG("RAMPSTAT %s:Left end-stop detected", dev->name); - stepper_trigger_callback(dev, STEPPER_EVENT_LEFT_END_STOP_DETECTED); - break; - - case TMC5XXX_STOP_RIGHT_EVENT: - LOG_DBG("RAMPSTAT %s:Right end-stop detected", dev->name); - stepper_trigger_callback(dev, STEPPER_EVENT_RIGHT_END_STOP_DETECTED); - break; - - case TMC5XXX_POS_REACHED_EVENT: - case TMC5XXX_POS_REACHED: - case TMC5XXX_POS_REACHED_AND_EVENT: - LOG_DBG("RAMPSTAT %s:Position reached", dev->name); - stepper_trigger_callback(dev, STEPPER_EVENT_STEPS_COMPLETED); - break; - - case TMC5XXX_STOP_SG_EVENT: - LOG_DBG("RAMPSTAT %s:Stall detected", dev->name); - stallguard_enable(dev, false); - stepper_trigger_callback(dev, STEPPER_EVENT_STALL_DETECTED); - break; - default: - LOG_ERR("Illegal ramp stat bit field 0x%x", ramp_stat_values); - break; - } - } else { - /* For SPI with DIAG0 pin, we use interrupt-driven approach */ - IF_ENABLED(TMC51XX_BUS_SPI, ({ - if (config->comm_type == TMC_COMM_SPI && config->diag0_gpio.port) { - /* Using interrupt-driven approach - no polling needed */ - return; - } - })) - - /* For UART or SPI without DIAG0, reschedule RAMPSTAT polling */ -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC - k_work_reschedule( - &stepper_data->rampstat_callback_dwork, - K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); -#endif /* CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC */ - } -} - -static void __maybe_unused tmc51xx_diag0_gpio_callback_handler(const struct device *port, - struct gpio_callback *cb, - gpio_port_pins_t pins) -{ - ARG_UNUSED(port); - ARG_UNUSED(pins); - - struct tmc51xx_data *stepper_data = CONTAINER_OF(cb, struct tmc51xx_data, diag0_cb); - - k_work_reschedule(&stepper_data->rampstat_callback_dwork, K_NO_WAIT); -} - -static int tmc51xx_stepper_enable(const struct device *dev) -{ - LOG_DBG("Enabling Stepper motor controller %s", dev->name); - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value); - if (err != 0) { - return -EIO; - } - - reg_value |= TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; - - return tmc51xx_write(dev, TMC51XX_CHOPCONF, reg_value); -} - -static int tmc51xx_stepper_disable(const struct device *dev) -{ - LOG_DBG("Disabling Stepper motor controller %s", dev->name); - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value); - if (err != 0) { - return -EIO; - } - - reg_value &= ~TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; - - return tmc51xx_write(dev, TMC51XX_CHOPCONF, reg_value); -} - -static int tmc51xx_stepper_is_moving(const struct device *dev, bool *is_moving) -{ - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_DRVSTATUS, ®_value); - - if (err != 0) { - LOG_ERR("%s: Failed to read DRVSTATUS register", dev->name); - return -EIO; - } - - *is_moving = (FIELD_GET(TMC5XXX_DRV_STATUS_STST_BIT, reg_value) != 1U); - LOG_DBG("Stepper motor controller %s is moving: %d", dev->name, *is_moving); - return 0; -} - -int tmc51xx_stepper_set_max_velocity(const struct device *dev, uint32_t velocity) -{ - const struct tmc51xx_config *config = dev->config; - const uint32_t clock_frequency = config->clock_frequency; - uint32_t velocity_fclk; - int err; - - velocity_fclk = tmc5xxx_calculate_velocity_from_hz_to_fclk(velocity, clock_frequency); - - err = tmc51xx_write(dev, TMC51XX_VMAX, velocity_fclk); - if (err != 0) { - LOG_ERR("%s: Failed to set max velocity", dev->name); - return -EIO; - } - return 0; -} - -static int tmc51xx_stepper_set_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution res) -{ - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value); - if (err != 0) { - return -EIO; - } - - reg_value &= ~TMC5XXX_CHOPCONF_MRES_MASK; - reg_value |= ((MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - LOG2(res)) - << TMC5XXX_CHOPCONF_MRES_SHIFT); - - err = tmc51xx_write(dev, TMC51XX_CHOPCONF, reg_value); - if (err != 0) { - return -EIO; - } - - LOG_DBG("Stepper motor controller %s set micro step resolution to 0x%x", dev->name, - reg_value); - return 0; -} - -static int tmc51xx_stepper_get_micro_step_res(const struct device *dev, - enum stepper_micro_step_resolution *res) -{ - uint32_t reg_value; - int err; - - err = tmc51xx_read(dev, TMC51XX_CHOPCONF, ®_value); - if (err != 0) { - return -EIO; - } - reg_value &= TMC5XXX_CHOPCONF_MRES_MASK; - reg_value >>= TMC5XXX_CHOPCONF_MRES_SHIFT; - *res = (1 << (MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - reg_value)); - LOG_DBG("Stepper motor controller %s get micro step resolution: %d", dev->name, *res); - return 0; -} - -static int tmc51xx_stepper_set_reference_position(const struct device *dev, const int32_t position) -{ - int err; - - err = tmc51xx_write(dev, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_HOLD_MODE); - if (err != 0) { - return -EIO; - } - - err = tmc51xx_write(dev, TMC51XX_XACTUAL, position); - if (err != 0) { - return -EIO; - } - LOG_DBG("Stepper motor controller %s set actual position to %d", dev->name, position); - return 0; -} - -static int read_actual_position(const struct device *dev, int32_t *position) -{ - const struct tmc51xx_config *config = dev->config; - - int err; - uint32_t raw_value; - - /* Check if device is using UART and is currently moving */ - if (config->comm_type == TMC_COMM_UART) { - bool is_moving; - - err = tmc51xx_stepper_is_moving(dev, &is_moving); - if (err != 0) { - return -EIO; - } - - if (is_moving) { - LOG_WRN("%s: Reading position while moving over UART is not supported", - dev->name); - return -ENOTSUP; - } - } - - err = tmc51xx_read(dev, TMC51XX_XACTUAL, &raw_value); - if (err != 0) { - return -EIO; - } - - *position = sign_extend(raw_value, TMC_RAMP_XACTUAL_SHIFT); - return 0; -} - -static int tmc51xx_stepper_get_actual_position(const struct device *dev, int32_t *position) -{ - int err; - - err = read_actual_position(dev, position); - if (err != 0) { - return -EIO; - } - LOG_DBG("%s actual position: %d", dev->name, *position); - return 0; -} - -static int tmc51xx_stepper_move_to(const struct device *dev, const int32_t micro_steps) -{ - LOG_DBG("%s set target position to %d", dev->name, micro_steps); - const struct tmc51xx_config *config = dev->config; - struct tmc51xx_data *data = dev->data; - int err; - - if (config->is_sg_enabled) { - stallguard_enable(dev, false); - } - - err = tmc51xx_write(dev, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_POSITIONING_MODE); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_XTARGET, micro_steps); - if (err != 0) { - return -EIO; - } - - if (config->is_sg_enabled) { - k_work_reschedule(&data->stallguard_dwork, - K_MSEC(config->sg_velocity_check_interval_ms)); - } - if (data->callback) { - /* For SPI with DIAG0 pin, we use interrupt-driven approach */ - IF_ENABLED(TMC51XX_BUS_SPI, ({ - if (config->comm_type == TMC_COMM_SPI && config->diag0_gpio.port) { - /* Using interrupt-driven approach - no polling needed */ - return 0; - } - })) - - /* For UART or SPI without DIAG0, reschedule RAMPSTAT polling */ -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC - k_work_reschedule( - &data->rampstat_callback_dwork, - K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); -#endif /* CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC */ - } - return 0; -} - -static int tmc51xx_stepper_move_by(const struct device *dev, const int32_t micro_steps) -{ - int err; - int32_t position; - - err = tmc51xx_stepper_get_actual_position(dev, &position); - if (err != 0) { - return -EIO; - } - int32_t target_position = position + micro_steps; - - LOG_DBG("%s moved to %d by steps: %d", dev->name, target_position, micro_steps); - - return tmc51xx_stepper_move_to(dev, target_position); -} - -static int tmc51xx_stepper_run(const struct device *dev, const enum stepper_direction direction) -{ - LOG_DBG("Stepper motor controller %s run", dev->name); - const struct tmc51xx_config *config = dev->config; - struct tmc51xx_data *data = dev->data; - int err; - - if (config->is_sg_enabled) { - err = stallguard_enable(dev, false); - if (err != 0) { - return -EIO; - } - } - - switch (direction) { - case STEPPER_DIRECTION_POSITIVE: - err = tmc51xx_write(dev, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE); - if (err != 0) { - return -EIO; - } - break; - - case STEPPER_DIRECTION_NEGATIVE: - err = tmc51xx_write(dev, TMC51XX_RAMPMODE, TMC5XXX_RAMPMODE_NEGATIVE_VELOCITY_MODE); - if (err != 0) { - return -EIO; - } - break; - } - - if (config->is_sg_enabled) { - k_work_reschedule(&data->stallguard_dwork, - K_MSEC(config->sg_velocity_check_interval_ms)); - } - if (data->callback) { - /* For SPI with DIAG0 pin, we use interrupt-driven approach */ - IF_ENABLED(TMC51XX_BUS_SPI, ({ - if (config->comm_type == TMC_COMM_SPI && config->diag0_gpio.port) { - /* Using interrupt-driven approach - no polling needed */ - return 0; - } - })) - - /* For UART or SPI without DIAG0, reschedule RAMPSTAT polling */ -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC - k_work_reschedule( - &data->rampstat_callback_dwork, - K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); -#endif /* CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC */ - } - return 0; -} - -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN - -int tmc51xx_stepper_set_ramp(const struct device *dev, - const struct tmc_ramp_generator_data *ramp_data) -{ - LOG_DBG("Stepper motor controller %s set ramp", dev->name); - int err; - - err = tmc51xx_write(dev, TMC51XX_VSTART, ramp_data->vstart); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_A1, ramp_data->a1); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_AMAX, ramp_data->amax); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_D1, ramp_data->d1); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_DMAX, ramp_data->dmax); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_V1, ramp_data->v1); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_VMAX, ramp_data->vmax); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_VSTOP, ramp_data->vstop); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_TZEROWAIT, ramp_data->tzerowait); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_THIGH, ramp_data->thigh); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_TCOOLTHRS, ramp_data->tcoolthrs); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_TPWMTHRS, ramp_data->tpwmthrs); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_TPOWER_DOWN, ramp_data->tpowerdown); - if (err != 0) { - return -EIO; - } - err = tmc51xx_write(dev, TMC51XX_IHOLD_IRUN, ramp_data->iholdrun); - if (err != 0) { - return -EIO; - } - return 0; -} - -#endif - -static int tmc51xx_init(const struct device *dev) -{ - LOG_DBG("TMC51XX stepper motor controller %s initialized", dev->name); - struct tmc51xx_data *data = dev->data; - const struct tmc51xx_config *config = dev->config; - int err; - - k_sem_init(&data->sem, 1, 1); - - err = tmc51xx_bus_check(dev); - if (err < 0) { - LOG_ERR("Bus not ready for '%s'", dev->name); - return err; - } - -#if TMC51XX_BUS_UART - /* Initialize SW_SEL GPIO if using UART and GPIO is specified */ - if (config->comm_type == TMC_COMM_UART && config->sw_sel_gpio.port) { - if (!gpio_is_ready_dt(&config->sw_sel_gpio)) { - LOG_ERR("SW_SEL GPIO not ready"); - return -ENODEV; - } - - err = gpio_pin_configure_dt(&config->sw_sel_gpio, GPIO_OUTPUT_ACTIVE); - if (err < 0) { - LOG_ERR("Failed to configure SW_SEL GPIO"); - return err; - } - } -#endif - - LOG_DBG("GCONF: %d", config->gconf); - err = tmc51xx_write(dev, TMC5XXX_GCONF, config->gconf); - if (err != 0) { - return -EIO; - } - - /* Read and write GSTAT register to clear any SPI Datagram errors. */ - uint32_t gstat_value; - - err = tmc51xx_read(dev, TMC5XXX_GSTAT, &gstat_value); - if (err != 0) { - return -EIO; - } - - err = tmc51xx_write(dev, TMC5XXX_GSTAT, gstat_value); - if (err != 0) { - return -EIO; - } - - if (config->is_sg_enabled) { - k_work_init_delayable(&data->stallguard_dwork, stallguard_work_handler); - - err = tmc51xx_write(dev, TMC51XX_SWMODE, BIT(10)); - if (err != 0) { - return -EIO; - } - - LOG_DBG("Setting stall guard to %d with delay %d ms", config->sg_threshold, - config->sg_velocity_check_interval_ms); - if (!IN_RANGE(config->sg_threshold, TMC5XXX_SG_MIN_VALUE, TMC5XXX_SG_MAX_VALUE)) { - LOG_ERR("Stallguard threshold out of range"); - return -EINVAL; - } - - int32_t stall_guard_threshold = (int32_t)config->sg_threshold; - - err = tmc51xx_write(dev, TMC51XX_COOLCONF, - stall_guard_threshold - << TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT); - if (err != 0) { - return -EIO; - } - k_work_reschedule(&data->stallguard_dwork, K_NO_WAIT); - } - -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN - err = tmc51xx_stepper_set_ramp(dev, &config->default_ramp_config); - if (err != 0) { - return -EIO; - } -#endif - - k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); - uint32_t rampstat_value; - (void)rampstat_read_clear(dev, &rampstat_value); - - err = tmc51xx_stepper_set_micro_step_res(dev, config->default_micro_step_res); - if (err != 0) { - return -EIO; - } - return 0; -} - -static DEVICE_API(stepper, tmc51xx_api) = { - .enable = tmc51xx_stepper_enable, - .disable = tmc51xx_stepper_disable, - .is_moving = tmc51xx_stepper_is_moving, - .move_by = tmc51xx_stepper_move_by, - .set_micro_step_res = tmc51xx_stepper_set_micro_step_res, - .get_micro_step_res = tmc51xx_stepper_get_micro_step_res, - .set_reference_position = tmc51xx_stepper_set_reference_position, - .get_actual_position = tmc51xx_stepper_get_actual_position, - .move_to = tmc51xx_stepper_move_to, - .run = tmc51xx_stepper_run, - .set_event_callback = tmc51xx_stepper_set_event_callback, -}; - -/* Initializes a struct tmc51xx_config for an instance on a SPI bus. */ -#define TMC51XX_CONFIG_SPI(inst) \ - .comm_type = TMC_COMM_SPI, \ - .bus.spi = SPI_DT_SPEC_INST_GET(inst, \ - (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_MODE_CPOL | \ - SPI_MODE_CPHA | SPI_WORD_SET(8)), \ - 0), \ - .bus_io = &tmc51xx_spi_bus_io, \ - .diag0_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, diag0_gpios, {0}) - -/* Initializes a struct tmc51xx_config for an instance on a UART bus. */ -#define TMC51XX_CONFIG_UART(inst) \ - .comm_type = TMC_COMM_UART, .bus.uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \ - .bus_io = &tmc51xx_uart_bus_io, .uart_addr = DT_INST_PROP_OR(inst, uart_device_addr, 1U), \ - .sw_sel_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, sw_sel_gpios, {0}) - -/* Device initialization macros */ -#define TMC51XX_DEFINE(inst) \ - BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) > 0), \ - "clock frequency must be non-zero positive value"); \ - static struct tmc51xx_data tmc51xx_data_##inst = { \ - .stepper = DEVICE_DT_GET(DT_DRV_INST(inst))}; \ - COND_CODE_1(DT_PROP_EXISTS(inst, stallguard_threshold_velocity), \ - BUILD_ASSERT(DT_PROP(inst, stallguard_threshold_velocity), \ - "stallguard threshold velocity must be a positive value"), ()); \ - IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(inst))); \ - static const struct tmc51xx_config tmc51xx_config_##inst = {COND_CODE_1 \ - (DT_INST_ON_BUS(inst, spi), \ - (TMC51XX_CONFIG_SPI(inst)), \ - (TMC51XX_CONFIG_UART(inst))), \ - .gconf = ((DT_INST_PROP(inst, en_pwm_mode) << TMC51XX_GCONF_EN_PWM_MODE_SHIFT) | \ - (DT_INST_PROP(inst, test_mode) << TMC51XX_GCONF_TEST_MODE_SHIFT) | \ - (DT_INST_PROP(inst, invert_direction) << TMC51XX_GCONF_SHAFT_SHIFT) | \ - (DT_INST_NODE_HAS_PROP(inst, diag0_gpios) \ - ? BIT(TMC51XX_GCONF_DIAG0_INT_PUSHPULL_SHIFT) \ - : 0)), \ - .clock_frequency = DT_INST_PROP(inst, clock_frequency), \ - .default_micro_step_res = DT_INST_PROP(inst, micro_step_res), \ - .sg_threshold = DT_INST_PROP(inst, stallguard2_threshold), \ - .sg_threshold_velocity = DT_INST_PROP(inst, stallguard_threshold_velocity), \ - .sg_velocity_check_interval_ms = \ - DT_INST_PROP(inst, stallguard_velocity_check_interval_ms), \ - .is_sg_enabled = DT_INST_PROP(inst, activate_stallguard2), \ - IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, \ - (.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC51XX(inst)))}; \ - DEVICE_DT_INST_DEFINE(inst, tmc51xx_init, NULL, &tmc51xx_data_##inst, \ - &tmc51xx_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY, \ - &tmc51xx_api); - -DT_INST_FOREACH_STATUS_OKAY(TMC51XX_DEFINE) diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.h b/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.h deleted file mode 100644 index 43c26255dfd85..0000000000000 --- a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC51XX_H -#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC51XX_H - -#include -#include - -#include - -#define DT_DRV_COMPAT adi_tmc51xx - -/* Check for supported bus types */ -#define TMC51XX_BUS_SPI DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) -#define TMC51XX_BUS_UART DT_ANY_INST_ON_BUS_STATUS_OKAY(uart) - -/* Common configuration structure for TMC51xx */ -struct tmc51xx_config { - union tmc_bus bus; - const struct tmc_bus_io *bus_io; - uint8_t comm_type; - const uint32_t gconf; - const uint32_t clock_frequency; - const uint16_t default_micro_step_res; - const int8_t sg_threshold; - const bool is_sg_enabled; - const uint32_t sg_velocity_check_interval_ms; - const uint32_t sg_threshold_velocity; -#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN - const struct tmc_ramp_generator_data default_ramp_config; -#endif -#if TMC51XX_BUS_UART - const struct gpio_dt_spec sw_sel_gpio; - uint8_t uart_addr; -#endif -#if TMC51XX_BUS_SPI - struct gpio_dt_spec diag0_gpio; -#endif -}; - -struct tmc51xx_data { - struct k_sem sem; - struct k_work_delayable stallguard_dwork; - struct k_work_delayable rampstat_callback_dwork; - struct gpio_callback diag0_cb; - const struct device *stepper; - stepper_event_callback_t callback; - void *event_cb_user_data; -}; - -#if TMC51XX_BUS_SPI -/* SPI bus I/O operations for TMC51xx devices */ -extern const struct tmc_bus_io tmc51xx_spi_bus_io; -#endif - -#if TMC51XX_BUS_UART -/* UART bus I/O operations for TMC51xx devices */ -extern const struct tmc_bus_io tmc51xx_uart_bus_io; -#endif - -#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC51XX_H */ diff --git a/drivers/stepper/adi_tmc/tmc5xxx/CMakeLists.txt b/drivers/stepper/adi_tmc/tmc5xxx/CMakeLists.txt new file mode 100644 index 0000000000000..48acdd19420cb --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc5xxx/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_sources(adi_tmc5xxx_core.c) + +# Common bus implementation sources +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_SPI tmc5xxx_spi.c) +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_UART tmc5xxx_uart.c) + +# Device-specific implementations +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC50XX tmc50xx.c) +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC51XX tmc51xx.c) + diff --git a/drivers/stepper/adi_tmc/tmc5xxx/adi_tmc5xxx_core.c b/drivers/stepper/adi_tmc/tmc5xxx/adi_tmc5xxx_core.c new file mode 100644 index 0000000000000..cc6297d0e77d2 --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc5xxx/adi_tmc5xxx_core.c @@ -0,0 +1,533 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "adi_tmc5xxx_core.h" +#include "adi_tmc_reg.h" + +#include + +LOG_MODULE_DECLARE(tmc5xxx, CONFIG_STEPPER_LOG_LEVEL); + +int tmc5xxx_bus_check(const struct device *dev) +{ + const struct tmc5xxx_controller_config *config = dev->config; + + return config->bus_io->check(&config->bus, config->comm_type); +} + +/** + * @brief Calculate the velocity in full clock cycles from the velocity in Hz + * + * @param velocity_hz Velocity in Hz + * @param clock_frequency Clock frequency in Hz + * + * @return Calculated velocity in full clock cycles + */ +static uint32_t tmc5xxx_calculate_velocity_from_hz_to_fclk(uint64_t velocity_hz, + uint32_t clock_frequency) +{ + __ASSERT_NO_MSG(clock_frequency); + return (uint32_t)(velocity_hz << TMC5XXX_CLOCK_FREQ_SHIFT) / clock_frequency; +} + +int tmc5xxx_controller_write_reg(const struct device *controller_dev, uint8_t reg, uint32_t value) +{ + const struct tmc5xxx_controller_config *config = controller_dev->config; + struct tmc5xxx_controller_data *data = controller_dev->data; + int err; + + k_sem_take(&data->bus_sem, K_FOREVER); + + LOG_DBG("%s: Writing 0x%08x to register 0x%02x", controller_dev->name, value, reg); + err = config->bus_io->write(controller_dev, reg, value); + + k_sem_give(&data->bus_sem); + + if (err != 0) { + LOG_ERR("%s: Failed to write register 0x%02x", controller_dev->name, reg); + return -EIO; + } + + return 0; +} + +int tmc5xxx_controller_read_reg(const struct device *controller_dev, uint8_t reg, uint32_t *value) +{ + const struct tmc5xxx_controller_config *config = controller_dev->config; + struct tmc5xxx_controller_data *data = controller_dev->data; + int err; + + k_sem_take(&data->bus_sem, K_FOREVER); + + err = config->bus_io->read(controller_dev, reg, value); + + k_sem_give(&data->bus_sem); + + if (err != 0) { + LOG_ERR("%s: Failed to read register 0x%02x", controller_dev->name, reg); + return -EIO; + } + + LOG_DBG("%s: Read 0x%08x from register 0x%02x", controller_dev->name, *value, reg); + return 0; +} + +int tmc5xxx_write_reg(const struct tmc5xxx_core_context *ctx, uint8_t reg, uint32_t value) +{ + const struct tmc5xxx_controller_config *config = ctx->controller_dev->config; + struct tmc5xxx_controller_data *controller_data = ctx->controller_dev->data; + int err; + + k_sem_take(&controller_data->bus_sem, K_FOREVER); + + LOG_DBG("%s: Writing 0x%08x to register 0x%02x", ctx->dev->name, value, reg); + err = config->bus_io->write(ctx->controller_dev, reg, value); + + k_sem_give(&controller_data->bus_sem); + + if (err != 0) { + LOG_ERR("%s: Failed to write register 0x%02x", ctx->dev->name, reg); + return -EIO; + } + + return 0; +} + +int tmc5xxx_read_reg(const struct tmc5xxx_core_context *ctx, uint8_t reg, uint32_t *value) +{ + const struct tmc5xxx_controller_config *config = ctx->controller_dev->config; + struct tmc5xxx_controller_data *controller_data = ctx->controller_dev->data; + + int err; + + k_sem_take(&controller_data->bus_sem, K_FOREVER); + + err = config->bus_io->read(ctx->controller_dev, reg, value); + + k_sem_give(&controller_data->bus_sem); + + if (err != 0) { + LOG_ERR("%s: Failed to read register 0x%02x", ctx->dev->name, reg); + return -EIO; + } + + LOG_DBG("%s: Read 0x%08x from register 0x%02x", ctx->dev->name, *value, reg); + return 0; +} + +int tmc5xxx_enable(const struct device *dev) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + + LOG_DBG("%s: Enabling stepper motor", ctx->dev->name); + uint32_t reg_value; + int err; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_CHOPCONF(ctx->motor_index), ®_value); + if (err != 0) { + return err; + } + + reg_value |= TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; + + return tmc5xxx_write_reg(ctx, TMC5XXX_CHOPCONF(ctx->motor_index), reg_value); +} + +int tmc5xxx_disable(const struct device *dev) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + + LOG_DBG("%s: Disabling stepper motor", ctx->dev->name); + uint32_t reg_value; + int err; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_CHOPCONF(ctx->motor_index), ®_value); + if (err != 0) { + return err; + } + + reg_value &= ~TMC5XXX_CHOPCONF_DRV_ENABLE_MASK; + + return tmc5xxx_write_reg(ctx, TMC5XXX_CHOPCONF(ctx->motor_index), reg_value); +} + +int tmc5xxx_is_moving(const struct device *dev, bool *is_moving) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + uint32_t reg_value; + int err; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_DRVSTATUS(ctx->motor_index), ®_value); + if (err != 0) { + LOG_ERR("%s: Failed to read DRVSTATUS register", ctx->dev->name); + return err; + } + + /* STST bit indicates if motor is standing still (1) or moving (0) */ + *is_moving = (FIELD_GET(TMC5XXX_DRV_STATUS_STST_BIT, reg_value) != 1U); + LOG_DBG("%s: Motor is %s", ctx->dev->name, *is_moving ? "moving" : "not moving"); + + return 0; +} + +int tmc5xxx_get_actual_position(const struct device *dev, int32_t *position) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + uint32_t raw_value; + int err; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_XACTUAL(ctx->motor_index), &raw_value); + if (err != 0) { + LOG_ERR("%s: Failed to read XACTUAL register", ctx->dev->name); + return err; + } + + /* Sign extend the position value */ + *position = sign_extend(raw_value, TMC_RAMP_XACTUAL_SHIFT); + LOG_DBG("%s: Actual position: %d", ctx->dev->name, *position); + + return 0; +} + +int tmc5xxx_set_reference_position(const struct device *dev, int32_t position) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + int err; + + err = tmc5xxx_write_reg(ctx, TMC5XXX_RAMPMODE(ctx->motor_index), + TMC5XXX_RAMPMODE_HOLD_MODE); + if (err != 0) { + return -EIO; + } + + err = tmc5xxx_write_reg(ctx, TMC5XXX_XACTUAL(ctx->motor_index), position); + if (err != 0) { + return -EIO; + } + LOG_DBG("%s: Setting reference position to %d", ctx->dev->name, position); + return 0; +} + +int tmc5xxx_stepper_set_max_velocity(const struct device *dev, uint32_t velocity) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + const struct tmc5xxx_controller_config *config = ctx->controller_dev->config; + + const uint32_t clock_frequency = config->clock_frequency; + uint32_t velocity_fclk; + int err; + + velocity_fclk = tmc5xxx_calculate_velocity_from_hz_to_fclk(velocity, clock_frequency); + + err = tmc5xxx_write_reg(ctx, TMC5XXX_VMAX(ctx->motor_index), velocity_fclk); + if (err != 0) { + LOG_ERR("%s: Failed to set max velocity", dev->name); + return -EIO; + } + return 0; +} + +int tmc5xxx_move_to(const struct device *dev, int32_t position) +{ + struct tmc5xxx_stepper_data *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + const struct tmc5xxx_stepper_config *config = dev->config; + int err; + + LOG_DBG("%s: Moving to position %d", ctx->dev->name, position); + + /* Disable stallguard if enabled */ + if (config->is_sg_enabled) { + tmc5xxx_stallguard_enable(dev, false); + } + + /* Set the ramp mode to positioning mode */ + err = tmc5xxx_write_reg(ctx, TMC5XXX_RAMPMODE(ctx->motor_index), + TMC5XXX_RAMPMODE_POSITIONING_MODE); + if (err != 0) { + return err; + } + + /* Clear any pending events in RAMPSTAT */ + uint32_t rampstat; + + err = tmc5xxx_rampstat_read_clear(dev, &rampstat); + if (err != 0) { + return err; + } + + /* Set the target position */ + err = tmc5xxx_write_reg(ctx, TMC5XXX_XTARGET(ctx->motor_index), (uint32_t)position); + if (err != 0) { + return err; + } + + /* Re-enable stallguard check if configured */ + if (config->is_sg_enabled) { + k_work_reschedule(&data->stallguard_dwork, + K_MSEC(config->sg_velocity_check_interval_ms)); + } + + /* Set up position monitoring if callback is registered */ + if (data->callback) { + const struct tmc5xxx_controller_config *ctrl_config = ctx->controller_dev->config; + + /* For SPI with DIAG0 pin, we use interrupt-driven approach */ + if (ctrl_config->comm_type == TMC_COMM_SPI && ctrl_config->diag0_gpio.port) { + /* Using interrupt-driven approach - no polling needed */ + return 0; + } + + /* For UART or SPI without DIAG0, schedule RAMPSTAT polling */ +#if defined(CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC) + k_work_reschedule(&data->rampstat_callback_dwork, + K_MSEC(CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC) + ); +#elif defined(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC) + k_work_reschedule(&data->rampstat_callback_dwork, + K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC) + ); +#endif + } + + return 0; +} + +int tmc5xxx_move_by(const struct device *dev, int32_t steps) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + int32_t current_pos; + int err; + + err = tmc5xxx_get_actual_position(dev, ¤t_pos); + if (err != 0) { + return err; + } + + LOG_DBG("%s: Moving by %d steps from position %d", ctx->dev->name, steps, current_pos); + return tmc5xxx_move_to(dev, current_pos + steps); +} + +int tmc5xxx_run(const struct device *dev, const enum stepper_direction direction) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + uint8_t ramp_mode; + + /* Set the appropriate ramp mode based on direction */ + if (direction == STEPPER_DIRECTION_POSITIVE) { + ramp_mode = TMC5XXX_RAMPMODE_POSITIVE_VELOCITY_MODE; + } else { + ramp_mode = TMC5XXX_RAMPMODE_NEGATIVE_VELOCITY_MODE; + } + + LOG_DBG("%s: Running in %s direction", ctx->dev->name, + (direction == STEPPER_DIRECTION_POSITIVE) ? "positive" : "negative"); + return tmc5xxx_write_reg(ctx, TMC5XXX_RAMPMODE(ctx->motor_index), ramp_mode); +} + +int tmc5xxx_set_micro_step_res(const struct device *dev, + const enum stepper_micro_step_resolution res) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + + if (!VALID_MICRO_STEP_RES(res)) { + LOG_ERR("Invalid micro step resolution %d", res); + return -ENOTSUP; + } + + uint32_t reg_value; + int err; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_CHOPCONF(ctx->motor_index), ®_value); + if (err != 0) { + return -EIO; + } + + reg_value &= ~TMC5XXX_CHOPCONF_MRES_MASK; + reg_value |= ((MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - LOG2(res)) + << TMC5XXX_CHOPCONF_MRES_SHIFT); + + err = tmc5xxx_write_reg(ctx, TMC5XXX_CHOPCONF(ctx->motor_index), reg_value); + if (err != 0) { + return -EIO; + } + + LOG_DBG("%s: Set microstep resolution to %d", ctx->dev->name, res); + + return 0; +} + +int tmc5xxx_get_micro_step_res(const struct device *dev, enum stepper_micro_step_resolution *res) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + uint32_t reg_value; + int err; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_CHOPCONF(ctx->motor_index), ®_value); + if (err != 0) { + return -EIO; + } + + reg_value &= TMC5XXX_CHOPCONF_MRES_MASK; + reg_value >>= TMC5XXX_CHOPCONF_MRES_SHIFT; + *res = (1 << (MICRO_STEP_RES_INDEX(STEPPER_MICRO_STEP_256) - reg_value)); + LOG_DBG("Stepper motor controller %s get micro step resolution: %d", dev->name, *res); + + LOG_DBG("%s: Current microstep resolution: %d", ctx->dev->name, *res); + return 0; +} + +int tmc5xxx_stallguard_enable(const struct device *dev, bool enable) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + const struct tmc5xxx_stepper_config *config = dev->config; + + uint32_t reg_value; + int err; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_SWMODE(ctx->motor_index), ®_value); + if (err != 0) { + return -EIO; + } + + if (enable) { + reg_value |= TMC5XXX_SW_MODE_SG_STOP_ENABLE; + int32_t actual_velocity; + + err = tmc5xxx_read_vactual(dev, &actual_velocity); + if (err != 0) { + return -EIO; + } + if (abs(actual_velocity) < config->sg_threshold_velocity) { + LOG_ERR("%s: StallGuard not enabled, actual velocity below threshold", + ctx->dev->name); + return -EAGAIN; + } + } else { + reg_value &= ~TMC5XXX_SW_MODE_SG_STOP_ENABLE; + } + + err = tmc5xxx_write_reg(ctx, TMC5XXX_SWMODE(ctx->motor_index), reg_value); + if (err != 0) { + LOG_ERR("%s: Failed to write SWMODE register", ctx->dev->name); + return -EIO; + } + LOG_DBG("%s: StallGuard %s", ctx->dev->name, enable ? "enabled" : "disabled"); + return 0; +} + +int tmc5xxx_read_vactual(const struct device *dev, int32_t *velocity) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + uint32_t raw_value; + int err; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_VACTUAL(ctx->motor_index), &raw_value); + if (err != 0) { + LOG_ERR("%s: Failed to read VACTUAL register", ctx->dev->name); + return err; + } + + /* Sign extend the velocity value */ + *velocity = sign_extend(raw_value, TMC_RAMP_VACTUAL_SHIFT); + + LOG_DBG("%s: Actual velocity: %d", ctx->dev->name, *velocity); + return 0; +} + +int tmc5xxx_rampstat_read_clear(const struct device *dev, uint32_t *rampstat) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + int err; + + /* Read the RAMPSTAT register */ + err = tmc5xxx_read_reg(ctx, TMC5XXX_RAMPSTAT(ctx->motor_index), rampstat); + if (err != 0) { + LOG_ERR("%s: Failed to read RAMPSTAT register", ctx->dev->name); + return err; + } + + /* Write back the value to clear the flags */ + err = tmc5xxx_write_reg(ctx, TMC5XXX_RAMPSTAT(ctx->motor_index), *rampstat); + if (err != 0) { + LOG_ERR("%s: Failed to clear RAMPSTAT register", ctx->dev->name); + return err; + } + + return 0; +} + +void tmc5xxx_trigger_callback(const struct device *dev, enum stepper_event event) +{ + struct tmc5xxx_stepper_data *data = dev->data; + + if (!data->callback) { + LOG_WRN_ONCE("No callback registered"); + return; + } + data->callback(data->core.dev, event, data->callback_user_data); +} + +void tmc5xxx_stallguard_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct tmc5xxx_stepper_data const *data = + CONTAINER_OF(dwork, struct tmc5xxx_stepper_data, stallguard_dwork); + const struct tmc5xxx_core_context *ctx = &data->core; + const struct tmc5xxx_stepper_config *config = data->core.dev->config; + int err; + + if (!config->is_sg_enabled) { + return; + } + + err = tmc5xxx_stallguard_enable(ctx->dev, true); + if (err == -EAGAIN) { + /* Velocity too low, reschedule check */ + k_work_reschedule(dwork, K_MSEC(config->sg_velocity_check_interval_ms)); + } +} + +#if defined(CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG) || \ + defined(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_STALLGUARD_LOG) + +void tmc5xxx_log_stallguard(struct tmc5xxx_stepper_data *const stepper_data, uint32_t drv_status) +{ + const struct tmc5xxx_core_context *ctx = &stepper_data->core; + int32_t position; + int err; + + err = tmc5xxx_get_actual_position(ctx->dev, &position); + if (err != 0) { + LOG_ERR("%s: Failed to read XACTUAL register", ctx->dev->name); + return; + } + + const uint8_t sg_result = FIELD_GET(TMC5XXX_DRV_STATUS_SG_RESULT_MASK, drv_status); + const bool sg_status = FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status); + + LOG_DBG("%s position: %d | sg result: %3d status: %d", ctx->dev->name, position, sg_result, + sg_status); +} + +#endif diff --git a/drivers/stepper/adi_tmc/tmc5xxx/adi_tmc5xxx_core.h b/drivers/stepper/adi_tmc/tmc5xxx/adi_tmc5xxx_core.h new file mode 100644 index 0000000000000..d2da718966f38 --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc5xxx/adi_tmc5xxx_core.h @@ -0,0 +1,164 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_ADI_TMC5XXX_CORE_H_ +#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_ADI_TMC5XXX_CORE_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Core context for stepper motor operations + * + * This structure contains all the necessary information to operate + * a stepper motor with a TMC5xxx controller. + */ +struct tmc5xxx_core_context { + const struct device *dev; /* Stepper device */ + const struct device *controller_dev; /* Parent controller device */ + uint8_t motor_index; /* Motor index (0 or 1) */ +}; + +/** + * @brief Controller data structure + */ +struct tmc5xxx_controller_data { + struct k_sem bus_sem; /* Semaphore for bus synchronization */ +}; + +/** + * @brief Controller configuration structure + */ +struct tmc5xxx_controller_config { + union tmc_bus bus; /* Bus connection (SPI/UART) */ + const struct tmc_bus_io *bus_io; /* Bus I/O operations */ + uint8_t comm_type; /* Communication type */ + uint32_t gconf; /* Global configuration register value */ + uint32_t clock_frequency; /* Clock frequency in Hz */ +#if defined(CONFIG_STEPPER_ADI_TMC_SPI) + const struct gpio_dt_spec diag0_gpio; /* GPIO specifications for DIAG0 */ +#endif + +#if defined(CONFIG_STEPPER_ADI_TMC_UART) + const struct gpio_dt_spec sw_sel_gpio; /* Switch select GPIO for UART mode */ + uint8_t uart_addr; /* UART slave address */ +#endif +}; + +/** + * @brief Stepper data structure + */ +struct tmc5xxx_stepper_data { + struct tmc5xxx_core_context core; /* Core context for this stepper */ + struct k_work_delayable stallguard_dwork; /* StallGuard work */ + struct k_work_delayable rampstat_callback_dwork; /* Rampstat work */ + struct gpio_callback diag0_cb; /* DIAG0 GPIO callback */ + stepper_event_callback_t callback; /* Event callback function */ + void *callback_user_data; /* User data for callback */ +}; + +/** + * @brief Stepper configuration structure + */ +struct tmc5xxx_stepper_config { + uint16_t default_micro_step_res; /* Default microstepping resolution */ + int8_t sg_threshold; /* StallGuard threshold */ + bool is_sg_enabled; /* StallGuard enabled flag */ + uint32_t sg_velocity_check_interval_ms; /* StallGuard velocity check interval */ + uint32_t sg_threshold_velocity; /* StallGuard threshold velocity */ + struct tmc_ramp_generator_data default_ramp_config; /* Default ramp configuration */ +}; + +/** + * @brief Check if the communication bus is ready + * + * @param dev Device pointer + * @return 0 on success, negative error code otherwise + */ +int tmc5xxx_bus_check(const struct device *dev); + +/** + * @brief Common register I/O functions + */ + +/** + * @brief Write to a register using the controller's bus + * + * @param controller_dev Controller device + * @param reg Register address + * @param value Value to write + * @return 0 on success, negative error code on failure + */ +int tmc5xxx_controller_write_reg(const struct device *controller_dev, uint8_t reg, uint32_t value); + +/** + * @brief Read from a register using the controller's bus + * + * @param controller_dev Controller device + * @param reg Register address + * @param value Pointer to store the read value + * @return 0 on success, negative error code on failure + */ +int tmc5xxx_controller_read_reg(const struct device *controller_dev, uint8_t reg, uint32_t *value); + +/** + * @brief Write to a register using the core context + * + * @param ctx Core context for the stepper + * @param reg Register address + * @param value Value to write + * @return 0 on success, negative error code otherwise + */ +int tmc5xxx_write_reg(const struct tmc5xxx_core_context *ctx, uint8_t reg, uint32_t value); + +/** + * @brief Read from a register using the core context + * + * @param ctx Core context for the stepper + * @param reg Register address + * @param value Pointer to store the read value + * @return 0 on success, negative error code otherwise + */ +int tmc5xxx_read_reg(const struct tmc5xxx_core_context *ctx, uint8_t reg, uint32_t *value); + +/** + * @brief Common stepper motor control functions + */ +int tmc5xxx_enable(const struct device *dev); +int tmc5xxx_disable(const struct device *dev); +int tmc5xxx_is_moving(const struct device *dev, bool *is_moving); +int tmc5xxx_get_actual_position(const struct device *dev, int32_t *position); +int tmc5xxx_set_reference_position(const struct device *dev, int32_t position); +int tmc5xxx_set_max_velocity(const struct device *dev, uint32_t velocity); +int tmc5xxx_move_to(const struct device *dev, int32_t position); +int tmc5xxx_move_by(const struct device *dev, int32_t steps); +int tmc5xxx_run(const struct device *dev, enum stepper_direction direction); +int tmc5xxx_set_micro_step_res(const struct device *dev, enum stepper_micro_step_resolution res); +int tmc5xxx_get_micro_step_res(const struct device *dev, enum stepper_micro_step_resolution *res); +int tmc5xxx_stallguard_enable(const struct device *dev, bool enable); +int tmc5xxx_rampstat_read_clear(const struct device *dev, uint32_t *rampstat); +void tmc5xxx_stallguard_work_handler(struct k_work *work); + +int tmc5xxx_read_vactual(const struct device *dev, int32_t *velocity); +void tmc5xxx_trigger_callback(const struct device *dev, enum stepper_event event); + +#if defined(CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG) || \ + defined(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_STALLGUARD_LOG) + +void tmc5xxx_log_stallguard(struct tmc5xxx_stepper_data *const stepper_data, uint32_t drv_status); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_ADI_TMC5XXX_CORE_H_ */ diff --git a/drivers/stepper/adi_tmc/adi_tmc_reg.h b/drivers/stepper/adi_tmc/tmc5xxx/adi_tmc_reg.h similarity index 75% rename from drivers/stepper/adi_tmc/adi_tmc_reg.h rename to drivers/stepper/adi_tmc/tmc5xxx/adi_tmc_reg.h index 0fa7dfa500249..fa83fe6751847 100644 --- a/drivers/stepper/adi_tmc/adi_tmc_reg.h +++ b/drivers/stepper/adi_tmc/tmc5xxx/adi_tmc_reg.h @@ -1,5 +1,5 @@ /** - * @file drivers/stepper/adi_tmc/adi_tmc_reg.h + * @file drivers/stepper/adi/tmc_reg.h * * @brief TMC Registers * @@ -21,8 +21,8 @@ extern "C" { /** Common Registers for TMC50XX and TMC51XX */ #if defined(CONFIG_STEPPER_ADI_TMC50XX) || defined(CONFIG_STEPPER_ADI_TMC51XX) -#define TMC5XXX_WRITE_BIT 0x80U -#define TMC5XXX_ADDRESS_MASK 0x7FU +#define TMC5XXX_WRITE_BIT 0x80U +#define TMC5XXX_ADDRESS_MASK 0x7FU #define TMC5XXX_CLOCK_FREQ_SHIFT 24 @@ -34,8 +34,8 @@ extern "C" { #define TMC5XXX_RAMPMODE_NEGATIVE_VELOCITY_MODE 2 #define TMC5XXX_RAMPMODE_HOLD_MODE 3 -#define TMC5XXX_SG_MIN_VALUE -64 -#define TMC5XXX_SG_MAX_VALUE 63 +#define TMC5XXX_SG_MIN_VALUE -64 +#define TMC5XXX_SG_MAX_VALUE 63 #define TMC5XXX_SW_MODE_SG_STOP_ENABLE BIT(10) #define TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT 16 @@ -70,7 +70,7 @@ extern "C" { #define TMC5XXX_POS_REACHED_AND_EVENT (TMC5XXX_POS_REACHED | TMC5XXX_POS_REACHED_EVENT) #define TMC5XXX_RAMPSTAT_STOP_SG_EVENT_MASK BIT(6) -#define TMC5XXX_STOP_SG_EVENT \ +#define TMC5XXX_STOP_SG_EVENT \ (TMC5XXX_RAMPSTAT_STOP_SG_EVENT_MASK >> TMC5XXX_RAMPSTAT_INT_SHIFT) #define TMC5XXX_RAMPSTAT_STOP_RIGHT_EVENT_MASK BIT(5) @@ -89,24 +89,25 @@ extern "C" { #define TMC50XX_MOTOR_ADDR(m) (0x20 << (m)) #define TMC50XX_MOTOR_ADDR_DRV(m) ((m) << 4) -#define TMC50XX_RAMPMODE(motor) (0x00 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_XACTUAL(motor) (0x01 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VACTUAL(motor) (0x02 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VSTART(motor) (0x03 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_A1(motor) (0x04 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_V1(motor) (0x05 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_AMAX(motor) (0x06 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VMAX(motor) (0x07 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_DMAX(motor) (0x08 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_D1(motor) (0x0A | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VSTOP(motor) (0x0B | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_TZEROWAIT(motor) (0x0C | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_XTARGET(motor) (0x0D | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_SWMODE(motor) (0x14 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_RAMPSTAT(motor) (0x15 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_CHOPCONF(motor) (0x6C | TMC50XX_MOTOR_ADDR_DRV(motor)) -#define TMC50XX_COOLCONF(motor) (0x6D | TMC50XX_MOTOR_ADDR_DRV(motor)) -#define TMC50XX_DRVSTATUS(motor) (0x6F | TMC50XX_MOTOR_ADDR_DRV(motor)) +#define TMC5XXX_RAMPMODE(motor) ((uint8_t)(0x00 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_XACTUAL(motor) ((uint8_t)(0x01 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_VACTUAL(motor) ((uint8_t)(0x02 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_VSTART(motor) ((uint8_t)(0x03 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_A1(motor) ((uint8_t)(0x04 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_V1(motor) ((uint8_t)(0x05 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_AMAX(motor) ((uint8_t)(0x06 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_VMAX(motor) ((uint8_t)(0x07 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_DMAX(motor) ((uint8_t)(0x08 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_D1(motor) ((uint8_t)(0x0A | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_VSTOP(motor) ((uint8_t)(0x0B | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_TZEROWAIT(motor) ((uint8_t)(0x0C | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_XTARGET(motor) ((uint8_t)(0x0D | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_SWMODE(motor) ((uint8_t)(0x14 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_RAMPSTAT(motor) ((uint8_t)(0x15 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC5XXX_CHOPCONF(motor) ((uint8_t)(0x6C | TMC50XX_MOTOR_ADDR_DRV(motor))) +#define TMC5XXX_COOLCONF(motor) ((uint8_t)(0x6D | TMC50XX_MOTOR_ADDR_DRV(motor))) +#define TMC5XXX_DRVSTATUS(motor) ((uint8_t)(0x6F | TMC50XX_MOTOR_ADDR_DRV(motor))) +#define TMC5XXX_IHOLD_IRUN(motor) ((uint8_t)(0x10 | TMC50XX_MOTOR_ADDR(motor))) #endif @@ -129,9 +130,8 @@ extern "C" { #define TMC50XX_PWMCONF(motor) (0x10 | TMC50XX_MOTOR_ADDR_PWM(motor)) #define TMC50XX_PWM_STATUS(motor) (0x11 | TMC50XX_MOTOR_ADDR_PWM(motor)) -#define TMC50XX_IHOLD_IRUN(motor) (0x10 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VCOOLTHRS(motor) (0x11 | TMC50XX_MOTOR_ADDR(motor)) -#define TMC50XX_VHIGH(motor) (0x12 | TMC50XX_MOTOR_ADDR(motor)) +#define TMC50XX_VCOOLTHRS(motor) ((uint8_t)(0x11 | TMC50XX_MOTOR_ADDR(motor))) +#define TMC50XX_VHIGH(motor) ((uint8_t)(0x12 | TMC50XX_MOTOR_ADDR(motor))) #define TMC50XX_XLATCH(motor) (0x16 | TMC50XX_MOTOR_ADDR(motor)) #define TMC50XX_MSLUT0(motor) (0x60 | TMC50XX_MOTOR_ADDR_DRV(motor)) diff --git a/drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.c b/drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.c new file mode 100644 index 0000000000000..b61c44ed7ff4f --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.c @@ -0,0 +1,335 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 Carl Zeiss Meditec AG + * SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +#include "tmc50xx.h" +#include "adi_tmc_reg.h" +#include "adi_tmc5xxx_core.h" + +#include +LOG_MODULE_REGISTER(tmc5xxx, CONFIG_STEPPER_LOG_LEVEL); + +static int tmc50xx_stepper_set_event_callback(const struct device *dev, + stepper_event_callback_t callback, void *user_data) +{ + struct tmc5xxx_stepper_data *data = dev->data; + + data->callback = callback; + data->callback_user_data = user_data; + return 0; +} + +static void rampstat_work_reschedule(struct k_work_delayable *rampstat_callback_dwork) +{ + k_work_reschedule(rampstat_callback_dwork, + K_MSEC(CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC)); +} + +static void rampstat_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + + struct tmc5xxx_stepper_data *stepper_data = + CONTAINER_OF(dwork, struct tmc5xxx_stepper_data, rampstat_callback_dwork); + const struct tmc5xxx_core_context *ctx = &stepper_data->core; + + __ASSERT_NO_MSG(stepper_config->controller); + + uint32_t drv_status; + int err; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_DRVSTATUS(ctx->motor_index), &drv_status); + + if (err != 0) { + LOG_ERR("%s: Failed to read DRVSTATUS register", ctx->dev->name); + return; + } +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMPSTAT_POLL_STALLGUARD_LOG + tmc5xxx_log_stallguard(stepper_data, drv_status); +#endif + if (FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status) == 1U) { + LOG_INF("%s: Stall detected", ctx->dev->name); + err = tmc5xxx_write_reg(ctx, TMC5XXX_RAMPMODE(ctx->motor_index), + TMC5XXX_RAMPMODE_HOLD_MODE); + if (err != 0) { + LOG_ERR("%s: Failed to stop motor", ctx->dev->name); + return; + } + } + + uint32_t rampstat_value; + + err = tmc5xxx_rampstat_read_clear(ctx->dev, &rampstat_value); + if (err != 0) { + LOG_ERR("%s: Failed to read RAMPSTAT register", ctx->dev->name); + return; + } + + const uint8_t ramp_stat_values = FIELD_GET(TMC5XXX_RAMPSTAT_INT_MASK, rampstat_value); + + if (ramp_stat_values > 0) { + switch (ramp_stat_values) { + case TMC5XXX_STOP_LEFT_EVENT: + LOG_DBG("RAMPSTAT %s:Left end-stop detected", ctx->dev->name); + tmc5xxx_trigger_callback(ctx->dev, STEPPER_EVENT_LEFT_END_STOP_DETECTED); + break; + + case TMC5XXX_STOP_RIGHT_EVENT: + LOG_DBG("RAMPSTAT %s:Right end-stop detected", ctx->dev->name); + tmc5xxx_trigger_callback(ctx->dev, STEPPER_EVENT_RIGHT_END_STOP_DETECTED); + break; + + case TMC5XXX_POS_REACHED_EVENT: + case TMC5XXX_POS_REACHED: + case TMC5XXX_POS_REACHED_AND_EVENT: + LOG_DBG("RAMPSTAT %s:Position reached", ctx->dev->name); + tmc5xxx_trigger_callback(ctx->dev, STEPPER_EVENT_STEPS_COMPLETED); + break; + + case TMC5XXX_STOP_SG_EVENT: + LOG_DBG("RAMPSTAT %s:Stall detected", ctx->dev->name); + tmc5xxx_stallguard_enable(ctx->dev, false); + tmc5xxx_trigger_callback(ctx->dev, STEPPER_EVENT_STALL_DETECTED); + break; + default: + LOG_ERR("Illegal ramp stat bit field"); + break; + } + } else { + rampstat_work_reschedule(&stepper_data->rampstat_callback_dwork); + } +} + +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN + +int tmc50xx_stepper_set_ramp(const struct device *dev, + const struct tmc_ramp_generator_data *ramp_data) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + + LOG_DBG("Stepper motor controller %s set ramp", dev->name); + int err; + + err = tmc5xxx_write_reg(ctx, TMC5XXX_VSTART(ctx->motor_index), ramp_data->vstart); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_A1(ctx->motor_index), ramp_data->a1); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_AMAX(ctx->motor_index), ramp_data->amax); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_D1(ctx->motor_index), ramp_data->d1); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_DMAX(ctx->motor_index), ramp_data->dmax); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_V1(ctx->motor_index), ramp_data->v1); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_VMAX(ctx->motor_index), ramp_data->vmax); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_VSTOP(ctx->motor_index), ramp_data->vstop); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_TZEROWAIT(ctx->motor_index), ramp_data->tzerowait); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC50XX_VHIGH(ctx->motor_index), ramp_data->vhigh); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC50XX_VCOOLTHRS(ctx->motor_index), ramp_data->vcoolthrs); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_IHOLD_IRUN(ctx->motor_index), ramp_data->iholdrun); + if (err != 0) { + return -EIO; + } + return 0; +} + +#endif + +static int tmc51xx_controller_init(const struct device *dev) +{ + LOG_DBG("TMC50XX stepper motor controller %s initialized", dev->name); + const struct tmc5xxx_controller_config *config = dev->config; + int err; + + err = tmc5xxx_bus_check(dev); + if (err < 0) { + LOG_ERR("Bus not ready for '%s'", dev->name); + return err; + } + + err = tmc5xxx_controller_write_reg(dev, TMC5XXX_GCONF, config->gconf); + if (err != 0) { + return -EIO; + } + + /* Read and write GSTAT register to clear any SPI Datagram errors. */ + uint32_t gstat_value; + + err = tmc5xxx_controller_read_reg(dev, TMC5XXX_GSTAT, &gstat_value); + if (err != 0) { + return -EIO; + } + + LOG_DBG("Device %s initialized", dev->name); + return 0; +} + +static int tmc50xx_stepper_init(const struct device *dev) +{ + struct tmc5xxx_stepper_data *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + const struct tmc5xxx_stepper_config *stepper_config = dev->config; + int err; + + if (stepper_config->is_sg_enabled) { + k_work_init_delayable(&data->stallguard_dwork, tmc5xxx_stallguard_work_handler); + + err = tmc5xxx_write_reg(ctx, TMC5XXX_SWMODE(ctx->motor_index), BIT(10)); + + if (err != 0) { + return -EIO; + } + + LOG_DBG("Setting stall guard to %d with delay %d ms", stepper_config->sg_threshold, + stepper_config->sg_velocity_check_interval_ms); + if (!IN_RANGE(stepper_config->sg_threshold, TMC5XXX_SG_MIN_VALUE, + TMC5XXX_SG_MAX_VALUE)) { + LOG_ERR("Stallguard threshold out of range"); + return -EINVAL; + } + + int32_t stall_guard_threshold = (int32_t)stepper_config->sg_threshold; + + err = tmc5xxx_write_reg(ctx, TMC5XXX_COOLCONF(ctx->motor_index), + stall_guard_threshold + << TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT); + if (err != 0) { + return -EIO; + } + k_work_reschedule(&data->stallguard_dwork, K_NO_WAIT); + } + +#ifdef CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN + err = tmc50xx_stepper_set_ramp(dev, &stepper_config->default_ramp_config); + if (err != 0) { + return -EIO; + } +#endif + + k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); + rampstat_work_reschedule(&data->rampstat_callback_dwork); + err = tmc5xxx_set_micro_step_res(dev, stepper_config->default_micro_step_res); + if (err != 0) { + return -EIO; + } + return 0; +} + +static DEVICE_API(stepper, tmc50xx_stepper_api) = { + .enable = tmc5xxx_enable, + .disable = tmc5xxx_disable, + .is_moving = tmc5xxx_is_moving, + .move_by = tmc5xxx_move_by, + .set_micro_step_res = tmc5xxx_set_micro_step_res, + .get_micro_step_res = tmc5xxx_get_micro_step_res, + .set_reference_position = tmc5xxx_set_reference_position, + .get_actual_position = tmc5xxx_get_actual_position, + .move_to = tmc5xxx_move_to, + .run = tmc5xxx_run, + .set_event_callback = tmc50xx_stepper_set_event_callback, +}; + +#define TMC50XX_SHAFT_CONFIG(child) \ + (DT_PROP(child, invert_direction) << TMC50XX_GCONF_SHAFT_SHIFT(DT_REG_ADDR(child))) | + +#define TMC50XX_STEPPER_DATA_DEFINE(child) \ + static struct tmc5xxx_stepper_data tmc5xxx_stepper_data_##child = { \ + .core = \ + { \ + .dev = DEVICE_DT_GET(child), \ + .controller_dev = DEVICE_DT_GET(DT_PARENT(child)), \ + .motor_index = DT_REG_ADDR(child), \ + }, \ + }; + +#define TMC50XX_STEPPER_CONFIG_DEFINE(child) \ + COND_CODE_1(DT_PROP_EXISTS(child, stallguard_threshold_velocity), \ + BUILD_ASSERT(DT_PROP(child, stallguard_threshold_velocity), \ + "stallguard threshold velocity must be a positive value"), ()); \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(child))); \ + static const struct tmc5xxx_stepper_config tmc5xxx_stepper_config_##child = { \ + .default_micro_step_res = DT_PROP(child, micro_step_res), \ + .sg_threshold = DT_PROP(child, stallguard2_threshold), \ + .sg_threshold_velocity = DT_PROP(child, stallguard_threshold_velocity), \ + .sg_velocity_check_interval_ms = \ + DT_PROP(child, stallguard_velocity_check_interval_ms), \ + .is_sg_enabled = DT_PROP(child, activate_stallguard2), \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC50XX_RAMP_GEN, \ + (.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC50XX(child))) }; + +#define TMC50XX_STEPPER_DEFINE(child) \ + DEVICE_DT_DEFINE(child, tmc50xx_stepper_init, NULL, &tmc5xxx_stepper_data_##child, \ + &tmc5xxx_stepper_config_##child, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &tmc50xx_stepper_api); + +#define TMC50XX_DEFINE(inst) \ + BUILD_ASSERT(DT_INST_CHILD_NUM(inst) <= 2, "tmc50xx can drive two steppers at max"); \ + BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) > 0), \ + "clock frequency must be non-zero positive value"); \ + \ + /* Controller data with bus semaphore */ \ + static struct tmc5xxx_controller_data tmc5xxx_controller_data_##inst = { \ + .bus_sem = Z_SEM_INITIALIZER((tmc5xxx_controller_data_##inst).bus_sem, 1, 1)}; \ + \ + /* Controller configuration */ \ + static const struct tmc5xxx_controller_config tmc5xxx_controller_config_##inst = { \ + .comm_type = TMC_COMM_SPI, \ + .bus.spi = SPI_DT_SPEC_INST_GET(inst, \ + (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | \ + SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8)), \ + 0), \ + .bus_io = &tmc5xxx_spi_bus_io, \ + .gconf = ((DT_INST_PROP(inst, poscmp_enable) \ + << TMC50XX_GCONF_POSCMP_ENABLE_SHIFT) | \ + (DT_INST_PROP(inst, test_mode) << TMC50XX_GCONF_TEST_MODE_SHIFT) | \ + DT_INST_FOREACH_CHILD(inst, TMC50XX_SHAFT_CONFIG)( \ + DT_INST_PROP(inst, lock_gconf) << TMC50XX_LOCK_GCONF_SHIFT)), \ + .clock_frequency = DT_INST_PROP(inst, clock_frequency)}; \ + \ + /* Define stepper configs, data, and devices for each child */ \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, TMC50XX_STEPPER_CONFIG_DEFINE); \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, TMC50XX_STEPPER_DATA_DEFINE); \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, TMC50XX_STEPPER_DEFINE); \ + \ + /* Define the controller device */ \ + DEVICE_DT_INST_DEFINE(inst, tmc51xx_controller_init, NULL, \ + &tmc5xxx_controller_data_##inst, &tmc5xxx_controller_config_##inst, \ + POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(TMC50XX_DEFINE) diff --git a/drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.h b/drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.h new file mode 100644 index 0000000000000..8853c51ef7aa6 --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC50XX_H +#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC50XX_H + +#include +#include + +#include +#include "tmc5xxx_bus.h" + +#define DT_DRV_COMPAT adi_tmc50xx + +/* Check for supported bus types - TMC50xx only supports SPI */ +#define TMC50XX_BUS_SPI TMC5XXX_BUS_SPI_CHECK(DT_DRV_COMPAT) + +/* Verify SPI bus is supported */ +#if !TMC50XX_BUS_SPI +#error "SPI bus is required for TMC50xx driver but not available" +#endif + +#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC50XX_H */ diff --git a/drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.c b/drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.c new file mode 100644 index 0000000000000..ab3392b84d11b --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.c @@ -0,0 +1,448 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Prevas A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +#include +#include "adi_tmc_reg.h" +#include "adi_tmc5xxx_core.h" + +#include "tmc51xx.h" +#include + +LOG_MODULE_REGISTER(tmc5xxx, CONFIG_STEPPER_LOG_LEVEL); + +static void rampstat_work_handler(struct k_work *work); +static void tmc51xx_diag0_gpio_callback_handler(const struct device *port, struct gpio_callback *cb, + gpio_port_pins_t pins); + +static int tmc51xx_stepper_set_event_callback(const struct device *dev, + stepper_event_callback_t callback, void *user_data) +{ + struct tmc5xxx_stepper_data *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + __maybe_unused const struct tmc5xxx_controller_config *config = ctx->controller_dev->config; + + __maybe_unused int err; + + data->callback = callback; + data->callback_user_data = user_data; + + /* Configure DIAG0 GPIO interrupt pin */ + IF_ENABLED(TMC51XX_BUS_SPI, ({ + if (config->comm_type == TMC_COMM_SPI && config->diag0_gpio.port) { + LOG_INF("Configuring DIAG0 GPIO interrupt pin"); + if (!gpio_is_ready_dt(&config->diag0_gpio)) { + LOG_ERR("DIAG0 interrupt GPIO not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&config->diag0_gpio, GPIO_INPUT); + if (err < 0) { + LOG_ERR("Could not configure DIAG0 GPIO (%d)", err); + return err; + } + k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); + + err = gpio_pin_interrupt_configure_dt(&config->diag0_gpio, GPIO_INT_EDGE_RISING); + if (err) { + LOG_ERR("failed to configure DIAG0 interrupt (err %d)", err); + return -EIO; + } + + /* Initialize and add GPIO callback */ + gpio_init_callback(&data->diag0_cb, tmc51xx_diag0_gpio_callback_handler, + BIT(config->diag0_gpio.pin)); + + err = gpio_add_callback(config->diag0_gpio.port, &data->diag0_cb); + if (err < 0) { + LOG_ERR("Could not add DIAG0 pin GPIO callback (%d)", err); + return -EIO; + } + + /* Clear any pending interrupts */ + uint32_t rampstat_value; + + err = tmc5xxx_rampstat_read_clear(dev, &rampstat_value); + if (err != 0) { + return -EIO; + } + }})) + + return 0; +} + +static void rampstat_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + + struct tmc5xxx_stepper_data *stepper_data = + CONTAINER_OF(dwork, struct tmc5xxx_stepper_data, rampstat_callback_dwork); + const struct tmc5xxx_core_context *ctx = &stepper_data->core; + __maybe_unused const struct tmc5xxx_stepper_config *config = stepper_data->core.dev->config; + int err; + uint32_t drv_status; + + err = tmc5xxx_read_reg(ctx, TMC5XXX_DRVSTATUS(ctx->motor_index), &drv_status); + if (err != 0) { + LOG_ERR("%s: Failed to read DRVSTATUS register", ctx->dev->name); + return; + } +#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_STALLGUARD_LOG + tmc5xxx_log_stallguard(dev, drv_status); +#endif + if (FIELD_GET(TMC5XXX_DRV_STATUS_SG_STATUS_MASK, drv_status) == 1U) { + LOG_INF("%s: Stall detected", ctx->dev->name); + err = tmc5xxx_write_reg(ctx, TMC5XXX_RAMPMODE(ctx->motor_index), + TMC5XXX_RAMPMODE_HOLD_MODE); + if (err != 0) { + LOG_ERR("%s: Failed to stop motor", ctx->dev->name); + return; + } + } + + uint32_t rampstat_value; + + err = tmc5xxx_rampstat_read_clear(ctx->dev, &rampstat_value); + if (err != 0) { + LOG_ERR("%s: Failed to read RAMPSTAT register", ctx->dev->name); + return; + } + + const uint8_t ramp_stat_values = FIELD_GET(TMC5XXX_RAMPSTAT_INT_MASK, rampstat_value); + + if (ramp_stat_values > 0) { + switch (ramp_stat_values) { + case TMC5XXX_STOP_LEFT_EVENT: + LOG_DBG("RAMPSTAT %s:Left end-stop detected", ctx->dev->name); + tmc5xxx_trigger_callback(ctx->dev, STEPPER_EVENT_LEFT_END_STOP_DETECTED); + break; + + case TMC5XXX_STOP_RIGHT_EVENT: + LOG_DBG("RAMPSTAT %s:Right end-stop detected", ctx->dev->name); + tmc5xxx_trigger_callback(ctx->dev, STEPPER_EVENT_RIGHT_END_STOP_DETECTED); + break; + + case TMC5XXX_POS_REACHED_EVENT: + case TMC5XXX_POS_REACHED: + case TMC5XXX_POS_REACHED_AND_EVENT: + LOG_DBG("RAMPSTAT %s:Position reached", ctx->dev->name); + tmc5xxx_trigger_callback(ctx->dev, STEPPER_EVENT_STEPS_COMPLETED); + break; + + case TMC5XXX_STOP_SG_EVENT: + LOG_DBG("RAMPSTAT %s:Stall detected", ctx->dev->name); + tmc5xxx_stallguard_enable(ctx->dev, false); + tmc5xxx_trigger_callback(ctx->dev, STEPPER_EVENT_STALL_DETECTED); + break; + default: + LOG_ERR("Illegal ramp stat bit field 0x%x", ramp_stat_values); + break; + } + } else { + /* For SPI with DIAG0 pin, we use interrupt-driven approach */ + IF_ENABLED(TMC51XX_BUS_SPI, ({ + const struct tmc5xxx_controller_config + *ctrl_config = ctx->controller_dev->config; + + if (ctrl_config->comm_type == TMC_COMM_SPI && + ctrl_config->diag0_gpio.port) { + /* Using interrupt-driven approach - no polling needed */ + return; + } + })) + + /* For UART or SPI without DIAG0, reschedule RAMPSTAT polling */ +#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC + k_work_reschedule(&stepper_data->rampstat_callback_dwork, + K_MSEC(CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC) + ); +#endif /* CONFIG_STEPPER_ADI_TMC51XX_RAMPSTAT_POLL_INTERVAL_IN_MSEC */ + } +} + +static void __maybe_unused tmc51xx_diag0_gpio_callback_handler(const struct device *port, + struct gpio_callback *cb, + gpio_port_pins_t pins) +{ + ARG_UNUSED(port); + ARG_UNUSED(pins); + + struct tmc5xxx_stepper_data *stepper_data = + CONTAINER_OF(cb, struct tmc5xxx_stepper_data, diag0_cb); + + k_work_reschedule(&stepper_data->rampstat_callback_dwork, K_NO_WAIT); +} + +#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN + +static int tmc51xx_stepper_set_ramp(const struct device *dev, + const struct tmc_ramp_generator_data *ramp_data) +{ + struct tmc5xxx_stepper_data const *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + + LOG_DBG("Stepper motor controller %s set ramp", dev->name); + int err; + + err = tmc5xxx_write_reg(ctx, TMC5XXX_VSTART(ctx->motor_index), ramp_data->vstart); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_A1(ctx->motor_index), ramp_data->a1); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_AMAX(ctx->motor_index), ramp_data->amax); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_D1(ctx->motor_index), ramp_data->d1); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_DMAX(ctx->motor_index), ramp_data->dmax); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_V1(ctx->motor_index), ramp_data->v1); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_VMAX(ctx->motor_index), ramp_data->vmax); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_VSTOP(ctx->motor_index), ramp_data->vstop); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_TZEROWAIT(ctx->motor_index), ramp_data->tzerowait); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC51XX_THIGH, ramp_data->thigh); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC51XX_TCOOLTHRS, ramp_data->tcoolthrs); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC51XX_TPWMTHRS, ramp_data->tpwmthrs); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC51XX_TPOWER_DOWN, ramp_data->tpowerdown); + if (err != 0) { + return -EIO; + } + err = tmc5xxx_write_reg(ctx, TMC5XXX_IHOLD_IRUN(ctx->motor_index), ramp_data->iholdrun); + + if (err != 0) { + return -EIO; + } + + return 0; +} + +#endif + +static int tmc51xx_controller_init(const struct device *dev) +{ + LOG_DBG("TMC51XX stepper motor controller %s initialized", dev->name); + const struct tmc5xxx_controller_config *config = dev->config; + int err; + + err = tmc5xxx_bus_check(dev); + if (err < 0) { + LOG_ERR("Bus not ready for '%s'", dev->name); + return err; + } + +#if TMC51XX_BUS_UART + /* Initialize SW_SEL GPIO if using UART and GPIO is specified */ + if (config->comm_type == TMC_COMM_UART && config->sw_sel_gpio.port) { + if (!gpio_is_ready_dt(&config->sw_sel_gpio)) { + LOG_ERR("SW_SEL GPIO not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&config->sw_sel_gpio, GPIO_OUTPUT_ACTIVE); + if (err < 0) { + LOG_ERR("Failed to configure SW_SEL GPIO"); + return err; + } + } +#endif + + err = tmc5xxx_controller_write_reg(dev, TMC5XXX_GCONF, config->gconf); + if (err != 0) { + return -EIO; + } + + /* Read and write GSTAT register to clear any SPI Datagram errors. */ + uint32_t gstat_value; + + err = tmc5xxx_controller_read_reg(dev, TMC5XXX_GSTAT, &gstat_value); + if (err != 0) { + return -EIO; + } + + LOG_DBG("Device %s initialized", dev->name); + return 0; +} + +static int tmc51xx_stepper_init(const struct device *dev) +{ + struct tmc5xxx_stepper_data *data = dev->data; + const struct tmc5xxx_core_context *ctx = &data->core; + const struct tmc5xxx_stepper_config *stepper_config = dev->config; + int err; + + if (stepper_config->is_sg_enabled) { + k_work_init_delayable(&data->stallguard_dwork, tmc5xxx_stallguard_work_handler); + + err = tmc5xxx_write_reg(ctx, TMC5XXX_SWMODE(ctx->motor_index), BIT(10)); + if (err != 0) { + return -EIO; + } + + LOG_DBG("Setting stall guard to %d with delay %d ms", stepper_config->sg_threshold, + stepper_config->sg_velocity_check_interval_ms); + if (!IN_RANGE(stepper_config->sg_threshold, TMC5XXX_SG_MIN_VALUE, + TMC5XXX_SG_MAX_VALUE)) { + LOG_ERR("Stallguard threshold out of range"); + return -EINVAL; + } + + int32_t stall_guard_threshold = (int32_t)stepper_config->sg_threshold; + + err = tmc5xxx_write_reg(ctx, TMC5XXX_COOLCONF(ctx->motor_index), + stall_guard_threshold + << TMC5XXX_COOLCONF_SG2_THRESHOLD_VALUE_SHIFT); + if (err != 0) { + return -EIO; + } + k_work_reschedule(&data->stallguard_dwork, K_NO_WAIT); + } + +#ifdef CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN + err = tmc51xx_stepper_set_ramp(dev, &stepper_config->default_ramp_config); + if (err != 0) { + return -EIO; + } +#endif + + k_work_init_delayable(&data->rampstat_callback_dwork, rampstat_work_handler); + uint32_t rampstat_value; + (void)tmc5xxx_rampstat_read_clear(dev, &rampstat_value); + + err = tmc5xxx_set_micro_step_res(dev, stepper_config->default_micro_step_res); + if (err != 0) { + return -EIO; + } + return 0; +} + +static DEVICE_API(stepper, tmc5xxx_stepper_api) = { + .enable = tmc5xxx_enable, + .disable = tmc5xxx_disable, + .is_moving = tmc5xxx_is_moving, + .move_by = tmc5xxx_move_by, + .set_micro_step_res = tmc5xxx_set_micro_step_res, + .get_micro_step_res = tmc5xxx_get_micro_step_res, + .set_reference_position = tmc5xxx_set_reference_position, + .get_actual_position = tmc5xxx_get_actual_position, + .move_to = tmc5xxx_move_to, + .run = tmc5xxx_run, + .set_event_callback = tmc51xx_stepper_set_event_callback, +}; + +/* Initializes a struct tmc51xx_config for an instance on SPI bus. */ +#define TMC51XX_CONFIG_SPI(inst) \ + .comm_type = TMC_COMM_SPI, \ + .bus.spi = SPI_DT_SPEC_INST_GET(inst, \ + (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_MODE_CPOL | \ + SPI_MODE_CPHA | SPI_WORD_SET(8)), \ + 0), \ + .bus_io = &tmc5xxx_spi_bus_io, \ + .diag0_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, diag0_gpios, {0}) + +/* Initializes a struct tmc51xx_config for an instance on UART bus. */ +#define TMC51XX_CONFIG_UART(inst) \ + .comm_type = TMC_COMM_UART, .bus.uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \ + .bus_io = &tmc5xxx_uart_bus_io, \ + .sw_sel_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, sw_sel_gpios, {0}), \ + .uart_addr = DT_INST_PROP_OR(inst, uart_device_addr, 1U) + +#define TMC5XXX_SHAFT_CONFIG(child) (DT_PROP(child, invert_direction) << TMC51XX_GCONF_SHAFT_SHIFT) + +#define TMC51XX_STEPPER_CONFIG_DEFINE(child) \ + COND_CODE_1(DT_PROP_EXISTS(child, stallguard_threshold_velocity), \ + BUILD_ASSERT(DT_PROP(child, stallguard_threshold_velocity), \ + "stallguard threshold velocity must be a positive value"), ()); \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, (CHECK_RAMP_DT_DATA(child))); \ + static const struct tmc5xxx_stepper_config tmc5xxx_stepper_config_##child = { \ + .default_micro_step_res = DT_PROP(child, micro_step_res), \ + .sg_threshold = DT_PROP(child, stallguard2_threshold), \ + .sg_threshold_velocity = DT_PROP(child, stallguard_threshold_velocity), \ + .sg_velocity_check_interval_ms = \ + DT_PROP(child, stallguard_velocity_check_interval_ms), \ + .is_sg_enabled = DT_PROP(child, activate_stallguard2), \ + IF_ENABLED(CONFIG_STEPPER_ADI_TMC51XX_RAMP_GEN, \ + (.default_ramp_config = TMC_RAMP_DT_SPEC_GET_TMC51XX(child))) }; + +#define TMC51XX_STEPPER_DATA_DEFINE(child) \ + static struct tmc5xxx_stepper_data tmc5xxx_stepper_data_##child = { \ + .core = \ + { \ + .dev = DEVICE_DT_GET(child), \ + .controller_dev = DEVICE_DT_GET(DT_PARENT(child)), \ + .motor_index = 0, \ + }, \ + }; + +#define TMC51XX_STEPPER_DEFINE(child) \ + DEVICE_DT_DEFINE(child, tmc51xx_stepper_init, NULL, &tmc5xxx_stepper_data_##child, \ + &tmc5xxx_stepper_config_##child, POST_KERNEL, \ + CONFIG_STEPPER_INIT_PRIORITY, &tmc5xxx_stepper_api); + +#define TMC51XX_DEFINE(inst) \ + BUILD_ASSERT((DT_INST_PROP(inst, clock_frequency) > 0), \ + "clock frequency must be non-zero positive value"); \ + \ + /* Controller data with bus semaphore */ \ + static struct tmc5xxx_controller_data tmc5xxx_controller_data_##inst = { \ + .bus_sem = Z_SEM_INITIALIZER((tmc5xxx_controller_data_##inst).bus_sem, 1, 1)}; \ + \ + /* Controller configuration */ \ + static const struct tmc5xxx_controller_config tmc5xxx_controller_config_##inst = { \ + COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ + (TMC51XX_CONFIG_SPI(inst)), (TMC51XX_CONFIG_UART(inst))), \ + .gconf = ((DT_INST_PROP(inst, en_pwm_mode) \ + << TMC51XX_GCONF_EN_PWM_MODE_SHIFT) | \ + (DT_INST_PROP(inst, test_mode) \ + << TMC51XX_GCONF_TEST_MODE_SHIFT) | \ + DT_INST_FOREACH_CHILD(inst, TMC5XXX_SHAFT_CONFIG) | \ + (DT_INST_NODE_HAS_PROP(inst, diag0_gpios) \ + ? BIT(TMC51XX_GCONF_DIAG0_INT_PUSHPULL_SHIFT) \ + : 0)), \ + .clock_frequency = DT_INST_PROP(inst, clock_frequency)}; \ + \ + /* Define stepper configs, data, and devices for each child */ \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, TMC51XX_STEPPER_CONFIG_DEFINE); \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, TMC51XX_STEPPER_DATA_DEFINE); \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, TMC51XX_STEPPER_DEFINE); \ + \ + /* Define the controller device */ \ + DEVICE_DT_INST_DEFINE(inst, tmc51xx_controller_init, NULL, \ + &tmc5xxx_controller_data_##inst, &tmc5xxx_controller_config_##inst, \ + POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(TMC51XX_DEFINE) diff --git a/drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.h b/drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.h new file mode 100644 index 0000000000000..befb99b29dd0c --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.h @@ -0,0 +1,32 @@ +/* +* SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC51XX_H +#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC51XX_H + +#include +#include + +#include +#include "tmc5xxx_bus.h" + +#define DT_DRV_COMPAT adi_tmc51xx + +/* Check for supported bus types */ +#if TMC5XXX_BUS_SPI_CHECK(DT_DRV_COMPAT) +#define TMC51XX_BUS_SPI 1 +#endif + +#if TMC5XXX_BUS_UART_CHECK(DT_DRV_COMPAT) +#define TMC51XX_BUS_UART 1 +#endif + +/* Verify at least one bus type is supported */ +#if !(TMC51XX_BUS_SPI || TMC51XX_BUS_UART) +#error "No supported bus types available for TMC51xx driver" +#endif + +#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC51XX_H */ diff --git a/drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_bus.h b/drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_bus.h new file mode 100644 index 0000000000000..742796698eb7b --- /dev/null +++ b/drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_bus.h @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_TMC5XXX_BUS_H +#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_TMC5XXX_BUS_H + +#include +#include +#include + +/* + * Custom macros to check if a compatible is on a specific bus + * These use the Kconfig variables that are already set by the build system + */ +#define TMC5XXX_BUS_SPI_CHECK(compat) \ + ((DT_HAS_COMPAT_STATUS_OKAY(compat)) && IS_ENABLED(CONFIG_STEPPER_ADI_TMC_SPI)) + +#define TMC5XXX_BUS_UART_CHECK(compat) \ + ((DT_HAS_COMPAT_STATUS_OKAY(compat)) && IS_ENABLED(CONFIG_STEPPER_ADI_TMC_UART)) + +/* Shared bus I/O operations for TMC5xxx devices */ +#if defined(CONFIG_STEPPER_ADI_TMC_SPI) +extern const struct tmc_bus_io tmc5xxx_spi_bus_io; +#endif + +#if defined(CONFIG_STEPPER_ADI_TMC_UART) +extern const struct tmc_bus_io tmc5xxx_uart_bus_io; +#endif + +#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_TMC5XXX_BUS_H */ diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_spi.c b/drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_spi.c similarity index 55% rename from drivers/stepper/adi_tmc/tmc51xx/tmc51xx_spi.c rename to drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_spi.c index 5a4871bd860ac..69834e0a7e7d7 100644 --- a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_spi.c +++ b/drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_spi.c @@ -9,13 +9,12 @@ #include #include -#include "tmc51xx.h" -#include "../adi_tmc_reg.h" +#include "adi_tmc_reg.h" +#include "adi_tmc5xxx_core.h" +#if CONFIG_STEPPER_ADI_TMC_SPI +LOG_MODULE_DECLARE(tmc5xxx, CONFIG_STEPPER_LOG_LEVEL); -#if TMC51XX_BUS_SPI -LOG_MODULE_DECLARE(tmc51xx, CONFIG_STEPPER_LOG_LEVEL); - -static int tmc51xx_bus_check_spi(const union tmc_bus *bus, uint8_t comm_type) +static int tmc5xxx_bus_check_spi(const union tmc_bus *bus, uint8_t comm_type) { if (comm_type != TMC_COMM_SPI) { return -ENOTSUP; @@ -23,10 +22,10 @@ static int tmc51xx_bus_check_spi(const union tmc_bus *bus, uint8_t comm_type) return spi_is_ready_dt(&bus->spi) ? 0 : -ENODEV; } -static int tmc51xx_reg_write_spi(const struct device *dev, const uint8_t reg_addr, +static int tmc5xxx_reg_write_spi(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val) { - const struct tmc51xx_config *config = dev->config; + const struct tmc5xxx_controller_config *config = dev->config; int err; err = tmc_spi_write_register(&config->bus.spi, TMC5XXX_WRITE_BIT, reg_addr, reg_val); @@ -37,9 +36,9 @@ static int tmc51xx_reg_write_spi(const struct device *dev, const uint8_t reg_add return err; } -static int tmc51xx_reg_read_spi(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) +static int tmc5xxx_reg_read_spi(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) { - const struct tmc51xx_config *config = dev->config; + const struct tmc5xxx_controller_config *config = dev->config; int err; err = tmc_spi_read_register(&config->bus.spi, TMC5XXX_ADDRESS_MASK, reg_addr, reg_val); @@ -50,9 +49,9 @@ static int tmc51xx_reg_read_spi(const struct device *dev, const uint8_t reg_addr return err; } -const struct tmc_bus_io tmc51xx_spi_bus_io = { - .check = tmc51xx_bus_check_spi, - .read = tmc51xx_reg_read_spi, - .write = tmc51xx_reg_write_spi, +const struct tmc_bus_io tmc5xxx_spi_bus_io = { + .check = tmc5xxx_bus_check_spi, + .read = tmc5xxx_reg_read_spi, + .write = tmc5xxx_reg_write_spi, }; -#endif /* TMC51XX_BUS_SPI */ +#endif /* CONFIG_STEPPER_ADI_TMC_SPI */ diff --git a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_uart.c b/drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_uart.c similarity index 62% rename from drivers/stepper/adi_tmc/tmc51xx/tmc51xx_uart.c rename to drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_uart.c index bc523bfe73356..0772ab474d724 100644 --- a/drivers/stepper/adi_tmc/tmc51xx/tmc51xx_uart.c +++ b/drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_uart.c @@ -7,13 +7,14 @@ #include #include "tmc51xx.h" +#include "adi_tmc5xxx_core.h" #include #include -#if TMC51XX_BUS_UART -LOG_MODULE_DECLARE(tmc51xx, CONFIG_STEPPER_LOG_LEVEL); +#if CONFIG_STEPPER_ADI_TMC_UART +LOG_MODULE_DECLARE(tmc5xxx, CONFIG_STEPPER_LOG_LEVEL); -static int tmc51xx_bus_check_uart(const union tmc_bus *bus, uint8_t comm_type) +static int tmc5xxx_bus_check_uart(const union tmc_bus *bus, uint8_t comm_type) { if (comm_type != TMC_COMM_UART) { return -ENOTSUP; @@ -21,10 +22,10 @@ static int tmc51xx_bus_check_uart(const union tmc_bus *bus, uint8_t comm_type) return device_is_ready(bus->uart) ? 0 : -ENODEV; } -static int tmc51xx_reg_write_uart(const struct device *dev, const uint8_t reg_addr, +static int tmc5xxx_reg_write_uart(const struct device *dev, const uint8_t reg_addr, const uint32_t reg_val) { - const struct tmc51xx_config *config = dev->config; + const struct tmc5xxx_controller_config *config = dev->config; int err; /* Route to the adi_tmc_uart.h implementation */ @@ -38,10 +39,10 @@ static int tmc51xx_reg_write_uart(const struct device *dev, const uint8_t reg_ad return err; } -static int tmc51xx_reg_read_uart(const struct device *dev, const uint8_t reg_addr, +static int tmc5xxx_reg_read_uart(const struct device *dev, const uint8_t reg_addr, uint32_t *reg_val) { - const struct tmc51xx_config *config = dev->config; + const struct tmc5xxx_controller_config *config = dev->config; int err; /* Route to the adi_tmc_uart.h implementation */ @@ -55,9 +56,9 @@ static int tmc51xx_reg_read_uart(const struct device *dev, const uint8_t reg_add return err; } -const struct tmc_bus_io tmc51xx_uart_bus_io = { - .check = tmc51xx_bus_check_uart, - .read = tmc51xx_reg_read_uart, - .write = tmc51xx_reg_write_uart, +const struct tmc_bus_io tmc5xxx_uart_bus_io = { + .check = tmc5xxx_bus_check_uart, + .read = tmc5xxx_reg_read_uart, + .write = tmc5xxx_reg_write_uart, }; -#endif /* TMC51XX_BUS_UART */ +#endif /* CONFIG_STEPPER_ADI_TMC_UART */ diff --git a/dts/bindings/stepper/adi/adi,tmc51xx-base.yaml b/dts/bindings/stepper/adi/adi,tmc51xx-base.yaml index dbdd40065c4a8..c8d25fbaa09b1 100644 --- a/dts/bindings/stepper/adi/adi,tmc51xx-base.yaml +++ b/dts/bindings/stepper/adi/adi,tmc51xx-base.yaml @@ -4,37 +4,6 @@ description: | Common properties for Analog Devices TMC51XX Stepper Motor Controller -include: - - name: adi,trinamic-gconf.yaml - property-allowlist: - - en-pwm-mode - - test-mode - - name: stepper-controller.yaml - - name: adi,trinamic-ramp-generator.yaml - property-allowlist: - - vstart - - a1 - - v1 - - amax - - vmax - - dmax - - d1 - - vstop - - tzerowait - - thigh - - tcoolthrs - - tpwmthrs - - tpowerdown - - ihold - - irun - - iholddelay - - name: adi,trinamic-stallguard.yaml - property-allowlist: - - activate-stallguard2 - - stallguard2-threshold - - stallguard-threshold-velocity - - stallguard-velocity-check-interval-ms - properties: "#address-cells": default: 1 @@ -53,3 +22,40 @@ properties: Hint: µstep velocity v[Hz] µsteps / s v[Hz] = v[51xx] * ( fCLK[Hz]/2 / 2^23 ) where v[51xx] is the value written to the TMC51XX. + +include: + - name: adi,trinamic-gconf.yaml + property-allowlist: + - en-pwm-mode + - test-mode + +child-binding: + include: + - name: stepper-controller.yaml + - name: base.yaml + property-allowlist: + - reg + - name: adi,trinamic-ramp-generator.yaml + property-allowlist: + - vstart + - a1 + - v1 + - amax + - vmax + - dmax + - d1 + - vstop + - tzerowait + - thigh + - tcoolthrs + - tpwmthrs + - tpowerdown + - ihold + - irun + - iholddelay + - name: adi,trinamic-stallguard.yaml + property-allowlist: + - activate-stallguard2 + - stallguard2-threshold + - stallguard-threshold-velocity + - stallguard-velocity-check-interval-ms diff --git a/include/zephyr/drivers/stepper/stepper_trinamic.h b/include/zephyr/drivers/stepper/stepper_trinamic.h index 7a27ef8de9c87..504e24c50db66 100644 --- a/include/zephyr/drivers/stepper/stepper_trinamic.h +++ b/include/zephyr/drivers/stepper/stepper_trinamic.h @@ -54,7 +54,7 @@ extern "C" { #define TMC_RAMP_IHOLD_IRUN_MIN 0 #define TMC_RAMP_IHOLDDELAY_MAX GENMASK(3, 0) #define TMC_RAMP_IHOLDDELAY_MIN 0 -#define TMC_RAMP_VACTUAL_SHIFT 22 +#define TMC_RAMP_VACTUAL_SHIFT 23 #define TMC_RAMP_XACTUAL_SHIFT 31 /* TMC50XX specific */ @@ -194,11 +194,11 @@ struct tmc_ramp_generator_data { #define TMC_RAMP_DT_SPEC_GET_TMC51XX(node) \ { \ - TMC_RAMP_DT_SPEC_GET_COMMON(DT_DRV_INST(node)) \ - .tpowerdown = DT_INST_PROP(node, tpowerdown), \ - .tpwmthrs = DT_INST_PROP(node, tpwmthrs), \ - .tcoolthrs = DT_INST_PROP(node, tcoolthrs), \ - .thigh = DT_INST_PROP(node, thigh), \ + TMC_RAMP_DT_SPEC_GET_COMMON(node) \ + .tpowerdown = DT_PROP(node, tpowerdown), \ + .tpwmthrs = DT_PROP(node, tpwmthrs), \ + .tcoolthrs = DT_PROP(node, tcoolthrs), \ + .thigh = DT_PROP(node, thigh), \ } /** diff --git a/tests/drivers/build_all/stepper/spi.dtsi b/tests/drivers/build_all/stepper/spi.dtsi index 0f6bfbc13db3e..7b5c4eb1b8ec6 100644 --- a/tests/drivers/build_all/stepper/spi.dtsi +++ b/tests/drivers/build_all/stepper/spi.dtsi @@ -16,7 +16,9 @@ adi_tmc50xx: adi_tmc50xx@0 { #address-cells = <1>; #size-cells = <0>; - poscmp-enable; test-mode; lock-gconf; /* ADI TMC Global configuration flags */ + poscmp-enable; + test-mode; + lock-gconf; /* ADI TMC Global configuration flags */ clock-frequency = <16000000>; /* Internal/External Clock frequency */ tmc50xx_0: tmc50xx_0@0 { @@ -29,9 +31,9 @@ adi_tmc50xx: adi_tmc50xx@0 { /* ADI TMC stallguard settings specific to TMC50XX */ activate-stallguard2; - stallguard-velocity-check-interval-ms=<100>; - stallguard2-threshold=<9>; - stallguard-threshold-velocity=<500000>; + stallguard-velocity-check-interval-ms = <100>; + stallguard2-threshold = <9>; + stallguard-threshold-velocity = <500000>; /* ADI TMC ramp generator as well as current settings */ vstart = <10>; @@ -59,9 +61,9 @@ adi_tmc50xx: adi_tmc50xx@0 { /* ADI TMC stallguard settings specific to TMC50XX */ activate-stallguard2; - stallguard-velocity-check-interval-ms=<100>; - stallguard2-threshold=<9>; - stallguard-threshold-velocity=<500000>; + stallguard-velocity-check-interval-ms = <100>; + stallguard2-threshold = <9>; + stallguard-threshold-velocity = <500000>; /* ADI TMC ramp generator as well as current settings */ vstart = <10>; @@ -90,35 +92,39 @@ adi_tmc51xx_1: adi_tmc51xx@1 { #address-cells = <1>; #size-cells = <0>; - en-pwm-mode; test-mode; /* ADI TMC Global configuration flags */ + en-pwm-mode; + test-mode; /* ADI TMC Global configuration flags */ clock-frequency = <16000000>; /* Internal/External Clock frequency */ - /* common stepper controller settings */ - invert-direction; - micro-step-res = <256>; - - /* ADI TMC stallguard settings specific to TMC5160 */ - activate-stallguard2; - stallguard-velocity-check-interval-ms = <100>; - stallguard2-threshold = <9>; - stallguard-threshold-velocity = <50000>; - - /* ADI TMC ramp generator as well as current settings */ - vstart = <10>; - a1 = <20>; - v1 = <30>; - d1 = <40>; - vmax = <50>; - amax = <60>; - dmax = <70>; - tzerowait = <80>; - thigh = <90>; - tcoolthrs = <100>; - tpwmthrs = <110>; - tpowerdown = <120>; - ihold = <1>; - irun = <2>; - iholddelay = <3>; + tmc51xx_1_motor: motor@0 { + status = "okay"; + reg = <0>; + invert-direction; + micro-step-res = <256>; + + /* ADI TMC stallguard settings specific to TMC5160 */ + activate-stallguard2; + stallguard-velocity-check-interval-ms = <100>; + stallguard2-threshold = <9>; + stallguard-threshold-velocity = <50000>; + + /* ADI TMC ramp generator as well as current settings */ + vstart = <10>; + a1 = <20>; + v1 = <30>; + d1 = <40>; + vmax = <50>; + amax = <60>; + dmax = <70>; + tzerowait = <80>; + thigh = <90>; + tcoolthrs = <100>; + tpwmthrs = <110>; + tpowerdown = <120>; + ihold = <1>; + irun = <2>; + iholddelay = <3>; + }; }; adi_tmc51xx_2: adi_tmc51xx@2 { @@ -132,33 +138,38 @@ adi_tmc51xx_2: adi_tmc51xx@2 { #size-cells = <0>; diag0-gpios = <&test_gpio 0x01 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; /* DIAG pin @0x01 */ - en-pwm-mode; test-mode; /* ADI TMC Global configuration flags */ + en-pwm-mode; + test-mode; /* ADI TMC Global configuration flags */ clock-frequency = <16000000>; /* Internal/External Clock frequency */ - /* common stepper controller settings */ - invert-direction; - micro-step-res = <256>; - - /* ADI TMC stallguard settings specific to TMC5160 */ - activate-stallguard2; - stallguard-velocity-check-interval-ms = <100>; - stallguard2-threshold = <9>; - stallguard-threshold-velocity = <50000>; - - /* ADI TMC ramp generator as well as current settings */ - vstart = <10>; - a1 = <20>; - v1 = <30>; - d1 = <40>; - vmax = <50>; - amax = <60>; - dmax = <70>; - tzerowait = <80>; - thigh = <90>; - tcoolthrs = <100>; - tpwmthrs = <110>; - tpowerdown = <120>; - ihold = <1>; - irun = <2>; - iholddelay = <3>; + tmc51xx_2_motor: motor@0 { + status = "okay"; + reg = <0>; + /* common stepper controller settings */ + invert-direction; + micro-step-res = <256>; + + /* ADI TMC stallguard settings specific to TMC5160 */ + activate-stallguard2; + stallguard-velocity-check-interval-ms = <100>; + stallguard2-threshold = <9>; + stallguard-threshold-velocity = <50000>; + + /* ADI TMC ramp generator as well as current settings */ + vstart = <10>; + a1 = <20>; + v1 = <30>; + d1 = <40>; + vmax = <50>; + amax = <60>; + dmax = <70>; + tzerowait = <80>; + thigh = <90>; + tcoolthrs = <100>; + tpwmthrs = <110>; + tpowerdown = <120>; + ihold = <1>; + irun = <2>; + iholddelay = <3>; + }; }; diff --git a/tests/drivers/build_all/stepper/uart.dtsi b/tests/drivers/build_all/stepper/uart.dtsi index e2116bae1f423..1f9c22c8ce5f6 100644 --- a/tests/drivers/build_all/stepper/uart.dtsi +++ b/tests/drivers/build_all/stepper/uart.dtsi @@ -8,11 +8,14 @@ adi_tmc51xx_uart: adi_tmc51xx { compatible = "adi,tmc51xx"; + #address-cells = <1>; + #size-cells = <0>; status = "okay"; label = "tmc5160_uart_1"; - en-pwm-mode; test-mode; /* ADI TMC Global configuration flags */ + en-pwm-mode; + test-mode; /* ADI TMC Global configuration flags */ clock-frequency = <16000000>; /* Internal/External Clock frequency */ /* common stepper controller settings */ @@ -20,25 +23,29 @@ adi_tmc51xx_uart: adi_tmc51xx { micro-step-res = <256>; /* ADI TMC stallguard settings specific to TMC5160 */ - activate-stallguard2; - stallguard-velocity-check-interval-ms = <100>; - stallguard2-threshold = <9>; - stallguard-threshold-velocity = <50000>; + adi_tmc51xx_uart_motor: motor@0 { + status = "okay"; + reg = <0>; + activate-stallguard2; + stallguard-velocity-check-interval-ms = <100>; + stallguard2-threshold = <9>; + stallguard-threshold-velocity = <50000>; - /* ADI TMC ramp generator as well as current settings */ - vstart = <10>; - a1 = <20>; - v1 = <30>; - d1 = <40>; - vmax = <50>; - amax = <60>; - dmax = <70>; - tzerowait = <80>; - thigh = <90>; - tcoolthrs = <100>; - tpwmthrs = <110>; - tpowerdown = <120>; - ihold = <1>; - irun = <2>; - iholddelay = <3>; + /* ADI TMC ramp generator as well as current settings */ + vstart = <10>; + a1 = <20>; + v1 = <30>; + d1 = <40>; + vmax = <50>; + amax = <60>; + dmax = <70>; + tzerowait = <80>; + thigh = <90>; + tcoolthrs = <100>; + tpwmthrs = <110>; + tpowerdown = <120>; + ihold = <1>; + irun = <2>; + iholddelay = <3>; + }; };