diff --git a/drivers/stepper/adi_tmc/bus/CMakeLists.txt b/drivers/stepper/adi_tmc/bus/CMakeLists.txt new file mode 100644 index 00000000000..fc10daf070d --- /dev/null +++ b/drivers/stepper/adi_tmc/bus/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_include_directories(include) + +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_SPI adi_tmc_spi.c) +zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_UART adi_tmc_uart.c) diff --git a/drivers/stepper/adi_tmc/adi_tmc_spi.c b/drivers/stepper/adi_tmc/bus/adi_tmc_spi.c similarity index 98% rename from drivers/stepper/adi_tmc/adi_tmc_spi.c rename to drivers/stepper/adi_tmc/bus/adi_tmc_spi.c index f9a29f2129e..ef75fe166a7 100644 --- a/drivers/stepper/adi_tmc/adi_tmc_spi.c +++ b/drivers/stepper/adi_tmc/bus/adi_tmc_spi.c @@ -4,7 +4,7 @@ */ #include -#include "adi_tmc_spi.h" +#include #define BUFFER_SIZE 5U diff --git a/drivers/stepper/adi_tmc/bus/adi_tmc_uart.c b/drivers/stepper/adi_tmc/bus/adi_tmc_uart.c new file mode 100644 index 00000000000..873ae36524b --- /dev/null +++ b/drivers/stepper/adi_tmc/bus/adi_tmc_uart.c @@ -0,0 +1,163 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(tmc_uart, CONFIG_STEPPER_LOG_LEVEL); + +/* TMC UART standard datagram size */ +#define ADI_TMC_UART_DATAGRAM_SIZE 8 +/* TMC UART read request datagram size */ +#define ADI_TMC_UART_READ_REQ_DATAGRAM_SIZE 4 +/* TMC UART protocol constants */ +#define ADI_TMC_UART_SYNC_BYTE 0x05 +#define ADI_TMC_UART_WRITE_BIT 0x80 + +uint8_t tmc_uart_calc_crc(const uint8_t *datagram, uint8_t len) +{ + uint8_t crc = 0; + + for (uint8_t i = 0; i < len; i++) { + uint8_t current_byte = datagram[i]; + + for (uint8_t j = 0; j < 8; j++) { + if ((crc >> 7) ^ (current_byte & 0x01)) { + crc = (crc << 1) ^ 0x07; + } else { + crc = (crc << 1) & 0xFF; + } + current_byte = current_byte >> 1; + } + } + + return crc; +} + +static int tmc_uart_send_byte_with_echo(const struct device *uart, uint8_t byte) +{ + uint8_t echo_byte; + int err; + + uart_poll_out(uart, byte); + + /* Wait for echo with timeout */ + k_timepoint_t end = sys_timepoint_calc(K_MSEC(5)); /* 5ms timeout for echo */ + + do { + err = uart_poll_in(uart, &echo_byte); + if (err >= 0 && echo_byte == byte) { + /* Received matching echo */ + return 0; + } + } while (err == -1 && !sys_timepoint_expired(end)); + + LOG_ERR("Echo mismatch or timeout: sent 0x%02X", byte); + return -EIO; +} + +int tmc_uart_write_register(const struct device *uart, uint8_t device_addr, + uint8_t register_address, uint32_t data) +{ + uint8_t buffer[ADI_TMC_UART_DATAGRAM_SIZE]; + int err = 0; + + /* Format the UART TMC datagram */ + buffer[0] = ADI_TMC_UART_SYNC_BYTE; /* Sync byte */ + buffer[1] = device_addr; /* Device address */ + buffer[2] = register_address | ADI_TMC_UART_WRITE_BIT; /* Register address with write bit */ + sys_put_be32(data, &buffer[3]); /* Write data */ + buffer[7] = tmc_uart_calc_crc(buffer, ADI_TMC_UART_DATAGRAM_SIZE - 1U); /* CRC */ + + /* Send datagram byte by byte using polling */ + for (size_t i = 0; i < ADI_TMC_UART_DATAGRAM_SIZE; i++) { + err = tmc_uart_send_byte_with_echo(uart, buffer[i]); + if (err) { + LOG_ERR("Failed to send byte %d: 0x%02X", i, buffer[i]); + return err; + } + } + + return 0; +} + +int tmc_uart_read_register(const struct device *uart, uint8_t device_addr, uint8_t register_address, + uint32_t *data) +{ + uint8_t write_buffer[ADI_TMC_UART_READ_REQ_DATAGRAM_SIZE]; + uint8_t read_buffer[ADI_TMC_UART_DATAGRAM_SIZE]; + struct uart_config uart_cfg; + + int err = 0; + + /* Get current UART configuration */ + err = uart_config_get(uart, &uart_cfg); + if (err) { + LOG_ERR("Failed to get UART configuration: %d", err); + return -EIO; + } + + /* Calculate delay based on UART baudrate (SENDDELAY: default=8 bit times))*/ + uint32_t delay_us = (8 * 1000000) / uart_cfg.baudrate; + + /* Format the UART TMC datagram for read */ + write_buffer[0] = ADI_TMC_UART_SYNC_BYTE; /* Sync byte */ + write_buffer[1] = device_addr; /* Device address */ + write_buffer[2] = register_address; /* Register address */ + write_buffer[3] = tmc_uart_calc_crc(write_buffer, 3); + + /* Send read request byte by byte and wait for each echo */ + for (size_t i = 0; i < ADI_TMC_UART_READ_REQ_DATAGRAM_SIZE; i++) { + err = tmc_uart_send_byte_with_echo(uart, write_buffer[i]); + if (err) { + LOG_ERR("Failed to send byte %d: 0x%02X", i, write_buffer[i]); + return -EIO; + } + } + + /* Small delay to allow device to prepare response */ + k_busy_wait(delay_us); + + /* Receive response with timeout */ + for (size_t i = 0; i < sizeof(read_buffer) && (err == 0); i++) { + k_timepoint_t end = sys_timepoint_calc(K_MSEC(1000)); + + do { + err = uart_poll_in(uart, &read_buffer[i]); + } while (err == -1 && !sys_timepoint_expired(end)); + + if (err == -1) { + LOG_ERR("Timeout waiting for byte %d for register 0x%x", i, + register_address); + return -EAGAIN; + } + if (err < 0) { + LOG_ERR("Error %d receiving byte %d for register 0x%x", err, i, + register_address); + return -EIO; + } + } + + /* Print the received bytes */ + LOG_HEXDUMP_DBG(read_buffer, ADI_TMC_UART_DATAGRAM_SIZE, "Received bytes:"); + + /* Validate CRC */ + uint8_t crc = tmc_uart_calc_crc(read_buffer, ADI_TMC_UART_DATAGRAM_SIZE - 1U); + + if (crc != read_buffer[7]) { + LOG_ERR("CRC mismatch for register 0x%x: got 0x%x, expected 0x%x", register_address, + read_buffer[7], crc); + return -EIO; + } + + /* Construct a 32-bit register value from received bytes */ + *data = sys_get_be32(&read_buffer[3]); + + return 0; +} diff --git a/drivers/stepper/adi_tmc/bus/include/adi_tmc_bus.h b/drivers/stepper/adi_tmc/bus/include/adi_tmc_bus.h new file mode 100644 index 00000000000..ed72a3ea6b6 --- /dev/null +++ b/drivers/stepper/adi_tmc/bus/include/adi_tmc_bus.h @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_H_ +#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_H_ + +#include +#include +#include + +/* Communication interface types */ +#define TMC_COMM_SPI 0 +#define TMC_COMM_UART 1 + +union tmc_bus { + struct spi_dt_spec spi; + const struct device *uart; +}; + +/** + * @brief Function pointer type for bus check function. + * @param bus Pointer to the bus structure. + * @param comm_type Communication type (SPI or UART). + * @return 0 if bus is ready, negative error code otherwise. + */ +typedef int (*tmc_bus_check_fn)(const union tmc_bus *bus, uint8_t comm_type); + +/** + * @brief Function pointer type for register read function. + * @param dev Pointer to the device structure. + * @param reg Register address to read. + * @param val Pointer to store the read value. + * @return 0 on success, negative error code otherwise. + */ +typedef int (*tmc_reg_read_fn)(const struct device *dev, uint8_t reg, uint32_t *val); + +/** + * @brief Function pointer type for register write function. + * @param dev Pointer to the device structure. + * @param reg Register address to write. + * @param val Value to write to the register. + * @return 0 on success, negative error code otherwise. + */ +typedef int (*tmc_reg_write_fn)(const struct device *dev, uint8_t reg, uint32_t val); + +/** + * @brief Structure for bus I/O operations. + * Contains function pointers for bus check, register read, and register write. + */ +struct tmc_bus_io { + tmc_bus_check_fn check; /* Function to check if the bus is ready */ + tmc_reg_read_fn read; /* Function to read a register */ + tmc_reg_write_fn write; /* Function to write to a register */ +}; + +#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_H_ */ diff --git a/drivers/stepper/adi_tmc/adi_tmc_spi.h b/drivers/stepper/adi_tmc/bus/include/adi_tmc_spi.h similarity index 85% rename from drivers/stepper/adi_tmc/adi_tmc_spi.h rename to drivers/stepper/adi_tmc/bus/include/adi_tmc_spi.h index c228b6192d4..a49a0a6ba59 100644 --- a/drivers/stepper/adi_tmc/adi_tmc_spi.h +++ b/drivers/stepper/adi_tmc/bus/include/adi_tmc_spi.h @@ -1,5 +1,5 @@ /** - * @file drivers/stepper/adi/stepper.h + * @file drivers/stepper/adi_tmc/bus/include/adi_tmc_spi.h * * @brief Private API for Trinamic SPI bus * @@ -10,8 +10,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_SPI_H_ -#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_SPI_H_ +#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_SPI_H_ +#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_SPI_H_ #ifdef __cplusplus extern "C" { @@ -60,4 +60,4 @@ int tmc_spi_write_register(const struct spi_dt_spec *bus, const uint8_t write_bi } #endif -#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_SPI_H_ */ +#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_SPI_H_ */ diff --git a/drivers/stepper/adi_tmc/bus/include/adi_tmc_uart.h b/drivers/stepper/adi_tmc/bus/include/adi_tmc_uart.h new file mode 100644 index 00000000000..db1888d6cf8 --- /dev/null +++ b/drivers/stepper/adi_tmc/bus/include/adi_tmc_uart.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_UART_H_ +#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_UART_H_ + +#include + +/** + * @brief Calculate CRC for TMC UART datagram + * + * @param datagram Pointer to datagram buffer + * @param len Length of datagram + * @return uint8_t CRC value + */ +uint8_t tmc_uart_calc_crc(const uint8_t *datagram, uint8_t len); + +/** + * @brief Read register via UART single-wire interface + * + * @param uart Pointer to UART device specification + * @param device_addr Secondary address of TMC device + * @param register_address Register address to read + * @param data Pointer to store read data + * @return int 0 on success, negative errno on failure + */ +int tmc_uart_read_register(const struct device *uart, uint8_t device_addr, uint8_t register_address, + uint32_t *data); + +/** + * @brief Write register via UART single-wire interface + * + * @param uart Pointer to UART device specification + * @param device_addr Secondary address of the TMC device + * @param register_address Register address to write + * @param data Data to write to register + * @return int 0 on success, negative errno on failure + */ +int tmc_uart_write_register(const struct device *uart, uint8_t device_addr, + uint8_t register_address, uint32_t data); + +#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_UART_H_ */