1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz <fabianblatz@gmail.com>
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <zephyr/drivers/counter.h>
7 #include "step_dir_stepper_common.h"
8 
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_DECLARE(step_dir_stepper);
11 
step_counter_top_interrupt(const struct device * dev,void * user_data)12 static void step_counter_top_interrupt(const struct device *dev, void *user_data)
13 {
14 	ARG_UNUSED(dev);
15 	struct step_dir_stepper_common_data *data = user_data;
16 
17 	stepper_handle_timing_signal(data->dev);
18 }
19 
step_counter_timing_source_update(const struct device * dev,const uint64_t microstep_interval_ns)20 int step_counter_timing_source_update(const struct device *dev,
21 				      const uint64_t microstep_interval_ns)
22 {
23 	const struct step_dir_stepper_common_config *config = dev->config;
24 	struct step_dir_stepper_common_data *data = dev->data;
25 	int ret;
26 
27 	if (microstep_interval_ns == 0) {
28 		return -EINVAL;
29 	}
30 
31 	data->counter_top_cfg.ticks = DIV_ROUND_UP(
32 		counter_get_frequency(config->counter) * microstep_interval_ns, NSEC_PER_SEC);
33 
34 	/* Lock interrupts while modifying counter settings */
35 	int key = irq_lock();
36 
37 	ret = counter_set_top_value(config->counter, &data->counter_top_cfg);
38 
39 	irq_unlock(key);
40 
41 	if (ret != 0) {
42 		LOG_ERR("%s: Failed to set counter top value (error: %d)", dev->name, ret);
43 		return ret;
44 	}
45 
46 	return 0;
47 }
48 
step_counter_timing_source_start(const struct device * dev)49 int step_counter_timing_source_start(const struct device *dev)
50 {
51 	const struct step_dir_stepper_common_config *config = dev->config;
52 	struct step_dir_stepper_common_data *data = dev->data;
53 	int ret;
54 
55 	ret = counter_start(config->counter);
56 	if (ret < 0 && ret != -EALREADY) {
57 		LOG_ERR("Failed to start counter: %d", ret);
58 		return ret;
59 	}
60 
61 	data->counter_running = true;
62 
63 	return 0;
64 }
65 
step_counter_timing_source_stop(const struct device * dev)66 int step_counter_timing_source_stop(const struct device *dev)
67 {
68 	const struct step_dir_stepper_common_config *config = dev->config;
69 	struct step_dir_stepper_common_data *data = dev->data;
70 	int ret;
71 
72 	ret = counter_stop(config->counter);
73 	if (ret < 0 && ret != -EALREADY) {
74 		LOG_ERR("Failed to stop counter: %d", ret);
75 		return ret;
76 	}
77 
78 	data->counter_running = false;
79 
80 	return 0;
81 }
82 
step_counter_timing_source_needs_reschedule(const struct device * dev)83 bool step_counter_timing_source_needs_reschedule(const struct device *dev)
84 {
85 	ARG_UNUSED(dev);
86 	return false;
87 }
88 
step_counter_timing_source_is_running(const struct device * dev)89 bool step_counter_timing_source_is_running(const struct device *dev)
90 {
91 	struct step_dir_stepper_common_data *data = dev->data;
92 
93 	return data->counter_running;
94 }
95 
step_counter_timing_source_init(const struct device * dev)96 int step_counter_timing_source_init(const struct device *dev)
97 {
98 	const struct step_dir_stepper_common_config *config = dev->config;
99 	struct step_dir_stepper_common_data *data = dev->data;
100 
101 	if (!device_is_ready(config->counter)) {
102 		LOG_ERR("Counter device is not ready");
103 		return -ENODEV;
104 	}
105 
106 	data->counter_top_cfg.callback = step_counter_top_interrupt;
107 	data->counter_top_cfg.user_data = data;
108 	data->counter_top_cfg.flags = 0;
109 	data->counter_top_cfg.ticks = counter_us_to_ticks(config->counter, 1000000);
110 
111 	return 0;
112 }
113 
114 const struct stepper_timing_source_api step_counter_timing_source_api = {
115 	.init = step_counter_timing_source_init,
116 	.update = step_counter_timing_source_update,
117 	.start = step_counter_timing_source_start,
118 	.needs_reschedule = step_counter_timing_source_needs_reschedule,
119 	.stop = step_counter_timing_source_stop,
120 	.is_running = step_counter_timing_source_is_running,
121 };
122