1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2024 Navimatix GmbH
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT ti_drv8424
7 
8 #include <zephyr/kernel.h>
9 #include <zephyr/drivers/stepper.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/drivers/stepper/stepper_drv8424.h>
12 #include "../step_dir/step_dir_stepper_common.h"
13 
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(drv8424, CONFIG_STEPPER_LOG_LEVEL);
16 
17 /**
18  * @brief DRV8424 stepper driver configuration data.
19  *
20  * This structure contains all of the devicetree specifications for the pins
21  * needed by a given DRV8424 stepper driver.
22  */
23 struct drv8424_config {
24 	struct step_dir_stepper_common_config common;
25 	struct gpio_dt_spec sleep_pin;
26 	struct gpio_dt_spec en_pin;
27 	struct gpio_dt_spec m0_pin;
28 	struct gpio_dt_spec m1_pin;
29 };
30 
31 /* Struct for storing the states of output pins. */
32 struct drv8424_pin_states {
33 	uint8_t sleep: 1;
34 	uint8_t en: 1;
35 	uint8_t m0: 2;
36 	uint8_t m1: 2;
37 };
38 
39 /**
40  * @brief DRV8424 stepper driver data.
41  *
42  * This structure contains mutable data used by a DRV8424 stepper driver.
43  */
44 struct drv8424_data {
45 	const struct step_dir_stepper_common_data common;
46 	bool enabled;
47 	struct drv8424_pin_states pin_states;
48 	enum stepper_micro_step_resolution ustep_res;
49 };
50 
51 STEP_DIR_STEPPER_STRUCT_CHECK(struct drv8424_config, struct drv8424_data);
52 
drv8424_set_microstep_pin(const struct device * dev,const struct gpio_dt_spec * pin,int value)53 static int drv8424_set_microstep_pin(const struct device *dev, const struct gpio_dt_spec *pin,
54 				     int value)
55 {
56 	int ret;
57 
58 	/* Reset microstep pin as it may have been disconnected. */
59 	ret = gpio_pin_configure_dt(pin, GPIO_OUTPUT_INACTIVE);
60 	if (ret != 0) {
61 		LOG_ERR("%s: Failed to reset micro-step pin (error: %d)", dev->name, ret);
62 		return ret;
63 	}
64 
65 	/* Set microstep pin */
66 	switch (value) {
67 	case 0:
68 		ret = gpio_pin_set_dt(pin, 0);
69 		break;
70 	case 1:
71 		ret = gpio_pin_set_dt(pin, 1);
72 		break;
73 	case 2:
74 		/* Hi-Z is set by configuring pin as disconnected, not
75 		 * all gpio controllers support this.
76 		 */
77 		ret = gpio_pin_configure_dt(pin, GPIO_DISCONNECTED);
78 		break;
79 	default:
80 		break;
81 	}
82 
83 	if (ret != 0) {
84 		LOG_ERR("%s: Failed to set micro-step pin (error: %d)", dev->name, ret);
85 		return ret;
86 	}
87 	return 0;
88 }
89 
90 /*
91  * If microstep setter fails, attempt to recover into previous state.
92  */
drv8424_microstep_recovery(const struct device * dev)93 int drv8424_microstep_recovery(const struct device *dev)
94 {
95 	const struct drv8424_config *config = dev->config;
96 	struct drv8424_data *data = dev->data;
97 	int ret;
98 
99 	uint8_t m0_value = data->pin_states.m0;
100 	uint8_t m1_value = data->pin_states.m1;
101 
102 	ret = drv8424_set_microstep_pin(dev, &config->m0_pin, m0_value);
103 	if (ret != 0) {
104 		LOG_ERR("%s: Failed to restore microstep configuration (error: %d)", dev->name,
105 			ret);
106 		return ret;
107 	}
108 
109 	ret = drv8424_set_microstep_pin(dev, &config->m1_pin, m1_value);
110 	if (ret != 0) {
111 		LOG_ERR("%s: Failed to restore microstep configuration (error: %d)", dev->name,
112 			ret);
113 		return ret;
114 	}
115 
116 	return 0;
117 }
118 
drv8424_enable(const struct device * dev,bool enable)119 static int drv8424_enable(const struct device *dev, bool enable)
120 {
121 	int ret;
122 	const struct drv8424_config *config = dev->config;
123 	struct drv8424_data *data = dev->data;
124 	bool has_enable_pin = config->en_pin.port != NULL;
125 	bool has_sleep_pin = config->sleep_pin.port != NULL;
126 
127 	/* Check availability of sleep and enable pins, as these might be hardwired. */
128 	if (!has_sleep_pin && !has_enable_pin) {
129 		LOG_ERR("%s: Failed to enable/disable device, neither sleep pin nor enable pin are "
130 			"available. The device is always on.",
131 			dev->name);
132 		return -ENOTSUP;
133 	}
134 
135 	if (has_sleep_pin) {
136 		ret = gpio_pin_set_dt(&config->sleep_pin, !enable);
137 		if (ret != 0) {
138 			LOG_ERR("%s: Failed to set sleep_pin (error: %d)", dev->name, ret);
139 			return ret;
140 		}
141 		data->pin_states.sleep = enable ? 0U : 1U;
142 	}
143 
144 	if (has_enable_pin) {
145 		ret = gpio_pin_set_dt(&config->en_pin, enable);
146 		if (ret != 0) {
147 			LOG_ERR("%s: Failed to set en_pin (error: %d)", dev->name, ret);
148 			return ret;
149 		}
150 		data->pin_states.en = enable ? 1U : 0U;
151 	}
152 
153 	data->enabled = enable;
154 	if (!enable) {
155 		config->common.timing_source->stop(dev);
156 		gpio_pin_set_dt(&config->common.step_pin, 0);
157 	}
158 
159 	return 0;
160 }
161 
drv8424_set_micro_step_res(const struct device * dev,enum stepper_micro_step_resolution micro_step_res)162 static int drv8424_set_micro_step_res(const struct device *dev,
163 				      enum stepper_micro_step_resolution micro_step_res)
164 {
165 	const struct drv8424_config *config = dev->config;
166 	struct drv8424_data *data = dev->data;
167 	int ret;
168 
169 	uint8_t m0_value = 0;
170 	uint8_t m1_value = 0;
171 
172 	/* 0: low
173 	 * 1: high
174 	 * 2: Hi-Z
175 	 * 3: 330kΩ
176 	 */
177 	switch (micro_step_res) {
178 	case STEPPER_MICRO_STEP_1:
179 		m0_value = 0;
180 		m1_value = 0;
181 		break;
182 	case STEPPER_MICRO_STEP_2:
183 		m0_value = 2;
184 		m1_value = 0;
185 		break;
186 	case STEPPER_MICRO_STEP_4:
187 		m0_value = 0;
188 		m1_value = 1;
189 		break;
190 	case STEPPER_MICRO_STEP_8:
191 		m0_value = 1;
192 		m1_value = 1;
193 		break;
194 	case STEPPER_MICRO_STEP_16:
195 		m0_value = 2;
196 		m1_value = 1;
197 		break;
198 	case STEPPER_MICRO_STEP_32:
199 		m0_value = 0;
200 		m1_value = 2;
201 		break;
202 	case STEPPER_MICRO_STEP_64:
203 		m0_value = 2;
204 		m1_value = 3;
205 		break;
206 	case STEPPER_MICRO_STEP_128:
207 		m0_value = 2;
208 		m1_value = 2;
209 		break;
210 	case STEPPER_MICRO_STEP_256:
211 		m0_value = 1;
212 		m1_value = 2;
213 		break;
214 	default:
215 		return -EINVAL;
216 	};
217 
218 	ret = drv8424_set_microstep_pin(dev, &config->m0_pin, m0_value);
219 	if (ret != 0) {
220 		return ret;
221 	}
222 
223 	ret = drv8424_set_microstep_pin(dev, &config->m1_pin, m1_value);
224 	if (ret != 0) {
225 		return ret;
226 	}
227 
228 	data->ustep_res = micro_step_res;
229 	data->pin_states.m0 = m0_value;
230 	data->pin_states.m1 = m1_value;
231 
232 	return 0;
233 }
234 
drv8424_get_micro_step_res(const struct device * dev,enum stepper_micro_step_resolution * micro_step_res)235 static int drv8424_get_micro_step_res(const struct device *dev,
236 				      enum stepper_micro_step_resolution *micro_step_res)
237 {
238 	struct drv8424_data *data = dev->data;
239 	*micro_step_res = data->ustep_res;
240 	return 0;
241 }
242 
drv8424_move_to(const struct device * dev,int32_t target)243 static int drv8424_move_to(const struct device *dev, int32_t target)
244 {
245 	struct drv8424_data *data = dev->data;
246 
247 	if (!data->enabled) {
248 		LOG_ERR("Failed to move to target position, device is not enabled");
249 		return -ECANCELED;
250 	}
251 
252 	return step_dir_stepper_common_move_to(dev, target);
253 }
254 
drv8424_move_by(const struct device * dev,int32_t steps)255 static int drv8424_move_by(const struct device *dev, int32_t steps)
256 {
257 	struct drv8424_data *data = dev->data;
258 
259 	if (!data->enabled) {
260 		LOG_ERR("Failed to move by delta, device is not enabled");
261 		return -ECANCELED;
262 	}
263 
264 	return step_dir_stepper_common_move_by(dev, steps);
265 }
266 
drv8424_run(const struct device * dev,enum stepper_direction direction,uint32_t velocity)267 static int drv8424_run(const struct device *dev, enum stepper_direction direction,
268 		       uint32_t velocity)
269 {
270 	struct drv8424_data *data = dev->data;
271 
272 	if (!data->enabled) {
273 		LOG_ERR("Failed to run stepper, device is not enabled");
274 		return -ECANCELED;
275 	}
276 
277 	return step_dir_stepper_common_run(dev, direction, velocity);
278 }
279 
drv8424_init(const struct device * dev)280 static int drv8424_init(const struct device *dev)
281 {
282 	const struct drv8424_config *const config = dev->config;
283 	struct drv8424_data *const data = dev->data;
284 	int ret;
285 
286 	/* Configure sleep pin if it is available */
287 	if (config->sleep_pin.port != NULL) {
288 		ret = gpio_pin_configure_dt(&config->sleep_pin, GPIO_OUTPUT_ACTIVE);
289 		if (ret != 0) {
290 			LOG_ERR("%s: Failed to configure sleep_pin (error: %d)", dev->name, ret);
291 			return ret;
292 		}
293 		data->pin_states.sleep = 1U;
294 	}
295 
296 	/* Configure enable pin if it is available */
297 	if (config->en_pin.port != NULL) {
298 		ret = gpio_pin_configure_dt(&config->en_pin, GPIO_OUTPUT_INACTIVE);
299 		if (ret != 0) {
300 			LOG_ERR("%s: Failed to configure en_pin (error: %d)", dev->name, ret);
301 			return ret;
302 		}
303 		data->pin_states.en = 0U;
304 	}
305 
306 	/* Configure microstep pin 0 */
307 	ret = gpio_pin_configure_dt(&config->m0_pin, GPIO_OUTPUT_INACTIVE);
308 	if (ret != 0) {
309 		LOG_ERR("%s: Failed to configure m0_pin (error: %d)", dev->name, ret);
310 		return ret;
311 	}
312 	data->pin_states.m0 = 0U;
313 
314 	/* Configure microstep pin 1 */
315 	ret = gpio_pin_configure_dt(&config->m1_pin, GPIO_OUTPUT_INACTIVE);
316 	if (ret != 0) {
317 		LOG_ERR("%s: Failed to configure m1_pin (error: %d)", dev->name, ret);
318 		return ret;
319 	}
320 	data->pin_states.m1 = 0U;
321 
322 	ret = drv8424_set_micro_step_res(dev, data->ustep_res);
323 	if (ret != 0) {
324 		return ret;
325 	}
326 
327 	ret = step_dir_stepper_common_init(dev);
328 	if (ret != 0) {
329 		LOG_ERR("Failed to initialize common step direction stepper (error: %d)", ret);
330 		return ret;
331 	}
332 
333 	return 0;
334 }
335 
336 static DEVICE_API(stepper, drv8424_stepper_api) = {
337 	.enable = drv8424_enable,
338 	.move_by = drv8424_move_by,
339 	.move_to = drv8424_move_to,
340 	.is_moving = step_dir_stepper_common_is_moving,
341 	.set_reference_position = step_dir_stepper_common_set_reference_position,
342 	.get_actual_position = step_dir_stepper_common_get_actual_position,
343 	.set_max_velocity = step_dir_stepper_common_set_max_velocity,
344 	.run = drv8424_run,
345 	.set_micro_step_res = drv8424_set_micro_step_res,
346 	.get_micro_step_res = drv8424_get_micro_step_res,
347 	.set_event_callback = step_dir_stepper_common_set_event_callback,
348 };
349 
350 #define DRV8424_DEVICE(inst)                                                                       \
351                                                                                                    \
352 	static const struct drv8424_config drv8424_config_##inst = {                               \
353 		.common = STEP_DIR_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst),                       \
354 		.sleep_pin = GPIO_DT_SPEC_INST_GET_OR(inst, sleep_gpios, {0}),                     \
355 		.en_pin = GPIO_DT_SPEC_INST_GET_OR(inst, en_gpios, {0}),                           \
356 		.m0_pin = GPIO_DT_SPEC_INST_GET(inst, m0_gpios),                                   \
357 		.m1_pin = GPIO_DT_SPEC_INST_GET(inst, m1_gpios),                                   \
358 	};                                                                                         \
359                                                                                                    \
360 	static struct drv8424_data drv8424_data_##inst = {                                         \
361 		.common = STEP_DIR_STEPPER_DT_INST_COMMON_DATA_INIT(inst),                         \
362 		.ustep_res = DT_INST_PROP(inst, micro_step_res),                                   \
363 	};                                                                                         \
364                                                                                                    \
365 	DEVICE_DT_INST_DEFINE(inst, &drv8424_init, NULL, &drv8424_data_##inst,                     \
366 			      &drv8424_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY,   \
367 			      &drv8424_stepper_api);
368 
369 DT_INST_FOREACH_STATUS_OKAY(DRV8424_DEVICE)
370