From ad4e4e9c3130c8f7b307af0a807c0e8a033fa843 Mon Sep 17 00:00:00 2001 From: Dipak Shetty Date: Sun, 22 Jun 2025 18:07:11 +0200 Subject: [PATCH 1/8] bindings: stepper :adi: stepper made into a child This follows the same pattern as the tmc50xx series (wherein the controller) had 2 motors. Now the tmx51xx also considers the motor as a child binding, enabling resuing the code between the 2 models. Signed-off-by: Dipak Shetty --- .../stepper/adi/adi,tmc51xx-base.yaml | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) 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 From a5ef4d88782c6793ff93ae71440f115bf965afaf Mon Sep 17 00:00:00 2001 From: Dipak Shetty Date: Sun, 22 Jun 2025 18:09:46 +0200 Subject: [PATCH 2/8] drivers: stepper: adi_tmc: tmc5xxx: create common bus handlers Create a common handler for handling bus transactions for both TMC5xxx stepper motor controllers. This consolidates the SPI and UART communication code in a shared implementation that can be used by both TMC50xx and TMC51xx controllers. Signed-off-by: Dipak Shetty --- drivers/stepper/adi_tmc/tmc51xx/tmc51xx.h | 65 ------------------- drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.h | 26 ++++++++ drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.h | 32 +++++++++ drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_bus.h | 33 ++++++++++ .../tmc51xx_spi.c => tmc5xxx/tmc5xxx_spi.c} | 29 ++++----- .../tmc51xx_uart.c => tmc5xxx/tmc5xxx_uart.c} | 25 +++---- 6 files changed, 118 insertions(+), 92 deletions(-) delete mode 100644 drivers/stepper/adi_tmc/tmc51xx/tmc51xx.h create mode 100644 drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.h create mode 100644 drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.h create mode 100644 drivers/stepper/adi_tmc/tmc5xxx/tmc5xxx_bus.h rename drivers/stepper/adi_tmc/{tmc51xx/tmc51xx_spi.c => tmc5xxx/tmc5xxx_spi.c} (55%) rename drivers/stepper/adi_tmc/{tmc51xx/tmc51xx_uart.c => tmc5xxx/tmc5xxx_uart.c} (62%) 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/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.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 */ From 544c195da199ba2f221c85109c338657b0abf33c Mon Sep 17 00:00:00 2001 From: Dipak Shetty Date: Sun, 22 Jun 2025 18:14:43 +0200 Subject: [PATCH 3/8] drivers: stepper: adi_tmc: tmc5xxx: create core handler The implementation moves the common bus transaction code from individual driver files into reusable core modules. The new adi_tmc5xxx_core files provide a unified API for basic controller operations like register read/write, motor control, and diagnostics. This refactoring also moves the register definitions to the tmc5xxx subdirectory and removes the no longer needed adi_tmc5xxx_common.h file. Signed-off-by: Dipak Shetty --- drivers/stepper/adi_tmc/adi_tmc5xxx_common.h | 51 -- .../adi_tmc/tmc5xxx/adi_tmc5xxx_core.c | 533 ++++++++++++++++++ .../adi_tmc/tmc5xxx/adi_tmc5xxx_core.h | 164 ++++++ .../adi_tmc/{ => tmc5xxx}/adi_tmc_reg.h | 54 +- 4 files changed, 724 insertions(+), 78 deletions(-) delete mode 100644 drivers/stepper/adi_tmc/adi_tmc5xxx_common.h create mode 100644 drivers/stepper/adi_tmc/tmc5xxx/adi_tmc5xxx_core.c create mode 100644 drivers/stepper/adi_tmc/tmc5xxx/adi_tmc5xxx_core.h rename drivers/stepper/adi_tmc/{ => tmc5xxx}/adi_tmc_reg.h (75%) 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/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)) From 7e5c138f4e1bb9946cbc287d2d9634aa57968bfc Mon Sep 17 00:00:00 2001 From: Dipak Shetty Date: Sun, 22 Jun 2025 18:18:48 +0200 Subject: [PATCH 4/8] drivers: stepper: adi_tmc: tmc5xxx: refactor to use core handler The drivers are now refactored to use the core module for handling the common functions. Rampstat polling, ramp setting and diagnostics are left to the individual drivers since they are very chip specific. Signed-off-by: Dipak Shetty --- drivers/stepper/adi_tmc/tmc50xx.c | 760 ------------------- drivers/stepper/adi_tmc/tmc51xx/tmc51xx.c | 847 ---------------------- drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.c | 335 +++++++++ drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.c | 448 ++++++++++++ 4 files changed, 783 insertions(+), 1607 deletions(-) delete mode 100644 drivers/stepper/adi_tmc/tmc50xx.c delete mode 100644 drivers/stepper/adi_tmc/tmc51xx/tmc51xx.c create mode 100644 drivers/stepper/adi_tmc/tmc5xxx/tmc50xx.c create mode 100644 drivers/stepper/adi_tmc/tmc5xxx/tmc51xx.c 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/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/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/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) From c8ffe14c0d773c71948084cd2ce044fbcee26818 Mon Sep 17 00:00:00 2001 From: Dipak Shetty Date: Sun, 22 Jun 2025 18:21:21 +0200 Subject: [PATCH 5/8] include: zephyr: drivers: stepper: refactor trinamic ramp get macro Since the motor is now made a child of the controller, the macro to pull the ramp parameters are now refactored to pull it out of the node. Signed-off-by: Dipak Shetty --- include/zephyr/drivers/stepper/stepper_trinamic.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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), \ } /** From cd7fa661cf1ba5852ad7befc77746eb07b426324 Mon Sep 17 00:00:00 2001 From: Dipak Shetty Date: Sun, 22 Jun 2025 18:22:36 +0200 Subject: [PATCH 6/8] drivers: stepper: adi_tmc: spi selection logic Update tmc50xx driver to conditionally select SPI support Signed-off-by: Dipak Shetty --- drivers/stepper/adi_tmc/Kconfig.tmc50xx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 7817c722ee63227f55ae2341ef0ebcc30276e053 Mon Sep 17 00:00:00 2001 From: Dipak Shetty Date: Sun, 22 Jun 2025 18:23:32 +0200 Subject: [PATCH 7/8] drivers: stepper: adi_tmc: cmake reorg reorganize CMakeLists for TMC5xxx support subsequent to the changes. Signed-off-by: Dipak Shetty --- drivers/stepper/adi_tmc/CMakeLists.txt | 6 ++++-- drivers/stepper/adi_tmc/tmc51xx/CMakeLists.txt | 4 ---- drivers/stepper/adi_tmc/tmc5xxx/CMakeLists.txt | 13 +++++++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) delete mode 100644 drivers/stepper/adi_tmc/tmc51xx/CMakeLists.txt create mode 100644 drivers/stepper/adi_tmc/tmc5xxx/CMakeLists.txt 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/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/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) + From 0c11185d39a02119dc78645c5740794fecc1d775 Mon Sep 17 00:00:00 2001 From: Dipak Shetty Date: Sun, 22 Jun 2025 18:41:40 +0200 Subject: [PATCH 8/8] tests: drivers: build_all: stepper: update tmc51xx nodes The dts layout is updated for tmc51xx nodes, since the motor is now made a child of the parent controller. Signed-off-by: Dipak Shetty --- tests/drivers/build_all/stepper/spi.dtsi | 133 ++++++++++++---------- tests/drivers/build_all/stepper/uart.dtsi | 49 ++++---- 2 files changed, 100 insertions(+), 82 deletions(-) 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>; + }; };