1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz <fabianblatz@gmail.com>
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include "../step_dir/step_dir_stepper_common.h"
7 
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_REGISTER(tmc22xx, CONFIG_STEPPER_LOG_LEVEL);
10 
11 #define MSX_PIN_COUNT       2
12 #define MSX_PIN_STATE_COUNT 4
13 
14 struct tmc22xx_config {
15 	struct step_dir_stepper_common_config common;
16 	const struct gpio_dt_spec enable_pin;
17 	const struct gpio_dt_spec *msx_pins;
18 	enum stepper_micro_step_resolution *msx_resolutions;
19 };
20 
21 struct tmc22xx_data {
22 	struct step_dir_stepper_common_data common;
23 	enum stepper_micro_step_resolution resolution;
24 };
25 
26 STEP_DIR_STEPPER_STRUCT_CHECK(struct tmc22xx_config, struct tmc22xx_data);
27 
tmc22xx_stepper_enable(const struct device * dev,const bool enable)28 static int tmc22xx_stepper_enable(const struct device *dev, const bool enable)
29 {
30 	const struct tmc22xx_config *config = dev->config;
31 
32 	LOG_DBG("Stepper motor controller %s %s", dev->name, enable ? "enabled" : "disabled");
33 	if (enable) {
34 		return gpio_pin_set_dt(&config->enable_pin, 1);
35 	} else {
36 		return gpio_pin_set_dt(&config->enable_pin, 0);
37 	}
38 }
39 
tmc22xx_stepper_set_micro_step_res(const struct device * dev,enum stepper_micro_step_resolution micro_step_res)40 static int tmc22xx_stepper_set_micro_step_res(const struct device *dev,
41 					      enum stepper_micro_step_resolution micro_step_res)
42 {
43 	struct tmc22xx_data *data = dev->data;
44 	const struct tmc22xx_config *config = dev->config;
45 	int ret;
46 
47 	if (!config->msx_pins) {
48 		LOG_ERR("Microstep resolution pins are not configured");
49 		return -ENODEV;
50 	}
51 
52 	for (uint8_t i = 0; i < MSX_PIN_STATE_COUNT; i++) {
53 		if (micro_step_res != config->msx_resolutions[i]) {
54 			continue;
55 		}
56 
57 		ret = gpio_pin_set_dt(&config->msx_pins[0], i & 0x01);
58 		if (ret < 0) {
59 			LOG_ERR("Failed to set MS1 pin: %d", ret);
60 			return ret;
61 		}
62 
63 		ret = gpio_pin_set_dt(&config->msx_pins[1], (i & 0x02) >> 1);
64 		if (ret < 0) {
65 			LOG_ERR("Failed to set MS2 pin: %d", ret);
66 			return ret;
67 		}
68 
69 		data->resolution = micro_step_res;
70 		return 0;
71 	}
72 
73 	LOG_ERR("Unsupported microstep resolution: %d", micro_step_res);
74 	return -EINVAL;
75 }
76 
tmc22xx_stepper_get_micro_step_res(const struct device * dev,enum stepper_micro_step_resolution * micro_step_res)77 static int tmc22xx_stepper_get_micro_step_res(const struct device *dev,
78 					      enum stepper_micro_step_resolution *micro_step_res)
79 {
80 	struct tmc22xx_data *data = dev->data;
81 
82 	*micro_step_res = data->resolution;
83 	return 0;
84 }
85 
tmc22xx_stepper_configure_msx_pins(const struct device * dev)86 static int tmc22xx_stepper_configure_msx_pins(const struct device *dev)
87 {
88 	const struct tmc22xx_config *config = dev->config;
89 	int ret;
90 
91 	for (uint8_t i = 0; i < MSX_PIN_COUNT; i++) {
92 		if (!gpio_is_ready_dt(&config->msx_pins[i])) {
93 			LOG_ERR("MSX pin %u are not ready", i);
94 			return -ENODEV;
95 		}
96 
97 		ret = gpio_pin_configure_dt(&config->msx_pins[i], GPIO_OUTPUT);
98 		if (ret < 0) {
99 			LOG_ERR("Failed to configure msx pin %u", i);
100 			return ret;
101 		}
102 	}
103 	return 0;
104 }
105 
tmc22xx_stepper_init(const struct device * dev)106 static int tmc22xx_stepper_init(const struct device *dev)
107 {
108 	const struct tmc22xx_config *config = dev->config;
109 	struct tmc22xx_data *data = dev->data;
110 	int ret;
111 
112 	if (!gpio_is_ready_dt(&config->enable_pin)) {
113 		LOG_ERR("GPIO pins are not ready");
114 		return -ENODEV;
115 	}
116 
117 	ret = gpio_pin_configure_dt(&config->enable_pin, GPIO_OUTPUT);
118 	if (ret < 0) {
119 		LOG_ERR("Failed to configure enable pin: %d", ret);
120 		return ret;
121 	}
122 
123 	if (config->msx_pins) {
124 		ret = tmc22xx_stepper_configure_msx_pins(dev);
125 		if (ret < 0) {
126 			LOG_ERR("Failed to configure MSX pins: %d", ret);
127 			return ret;
128 		}
129 
130 		ret = tmc22xx_stepper_set_micro_step_res(dev, data->resolution);
131 		if (ret < 0) {
132 			LOG_ERR("Failed to set microstep resolution: %d", ret);
133 			return ret;
134 		}
135 	}
136 
137 	ret = step_dir_stepper_common_init(dev);
138 	if (ret < 0) {
139 		LOG_ERR("Failed to init step dir common stepper: %d", ret);
140 		return ret;
141 	}
142 
143 	return 0;
144 }
145 
146 static DEVICE_API(stepper, tmc22xx_stepper_api) = {
147 	.enable = tmc22xx_stepper_enable,
148 	.move_by = step_dir_stepper_common_move_by,
149 	.is_moving = step_dir_stepper_common_is_moving,
150 	.set_reference_position = step_dir_stepper_common_set_reference_position,
151 	.get_actual_position = step_dir_stepper_common_get_actual_position,
152 	.move_to = step_dir_stepper_common_move_to,
153 	.set_max_velocity = step_dir_stepper_common_set_max_velocity,
154 	.run = step_dir_stepper_common_run,
155 	.set_event_callback = step_dir_stepper_common_set_event_callback,
156 	.set_micro_step_res = tmc22xx_stepper_set_micro_step_res,
157 	.get_micro_step_res = tmc22xx_stepper_get_micro_step_res,
158 };
159 
160 #define TMC22XX_STEPPER_DEFINE(inst, msx_table)                                                    \
161 	IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, msx_gpios), (                                       \
162 	static const struct gpio_dt_spec tmc22xx_stepper_msx_pins_##inst[] = {                     \
163 		DT_INST_FOREACH_PROP_ELEM_SEP(                                                     \
164 			inst, msx_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)                              \
165 		),                                                                                 \
166 	};                                                                                         \
167 	BUILD_ASSERT(                                                                              \
168 		ARRAY_SIZE(tmc22xx_stepper_msx_pins_##inst) == MSX_PIN_COUNT,                      \
169 		"Two microstep config pins needed");                                               \
170 	))                                                                                         \
171                                                                                                    \
172 	static const struct tmc22xx_config tmc22xx_config_##inst = {                               \
173 		.common = STEP_DIR_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst),                       \
174 		.enable_pin = GPIO_DT_SPEC_INST_GET(inst, en_gpios),	                           \
175 		.msx_resolutions = msx_table,                                                      \
176 		IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, msx_gpios),				   \
177 		(.msx_pins = tmc22xx_stepper_msx_pins_##inst))					   \
178 	};                                                                                         \
179 	static struct tmc22xx_data tmc22xx_data_##inst = {                                         \
180 		.common = STEP_DIR_STEPPER_DT_INST_COMMON_DATA_INIT(inst),                         \
181 		.resolution = DT_INST_PROP(inst, micro_step_res),                                  \
182 	};                                                                                         \
183 	DEVICE_DT_INST_DEFINE(inst, tmc22xx_stepper_init, NULL, &tmc22xx_data_##inst,              \
184 			      &tmc22xx_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY,   \
185 			      &tmc22xx_stepper_api);
186 
187 #define DT_DRV_COMPAT adi_tmc2209
188 static enum stepper_micro_step_resolution tmc2209_msx_resolutions[MSX_PIN_STATE_COUNT] = {
189 	STEPPER_MICRO_STEP_8,
190 	STEPPER_MICRO_STEP_32,
191 	STEPPER_MICRO_STEP_64,
192 	STEPPER_MICRO_STEP_16,
193 };
194 DT_INST_FOREACH_STATUS_OKAY_VARGS(TMC22XX_STEPPER_DEFINE, tmc2209_msx_resolutions)
195 #undef DT_DRV_COMPAT
196