1 /*
2  * Copyright (c) 2022, Prevas A/S
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT nxp_mcux_qdec
7 
8 #include <errno.h>
9 #include <stdint.h>
10 
11 #include <fsl_enc.h>
12 #include <fsl_xbara.h>
13 
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/drivers/sensor.h>
16 #include <zephyr/drivers/sensor/qdec_mcux.h>
17 #include <zephyr/drivers/spi.h>
18 #include <zephyr/logging/log.h>
19 
20 LOG_MODULE_REGISTER(qdec_mcux, CONFIG_SENSOR_LOG_LEVEL);
21 
22 struct qdec_mcux_config {
23 	ENC_Type *base;
24 	const struct pinctrl_dev_config *pincfg;
25 	XBARA_Type *xbar;
26 	size_t xbar_maps_len;
27 	int xbar_maps[];
28 };
29 
30 struct qdec_mcux_data {
31 	enc_config_t qdec_config;
32 	int32_t position;
33 	uint16_t counts_per_revolution;
34 };
35 
int_to_work_mode(int32_t val)36 static enc_decoder_work_mode_t int_to_work_mode(int32_t val)
37 {
38 	return val == 0 ? kENC_DecoderWorkAsNormalMode :
39 			  kENC_DecoderWorkAsSignalPhaseCountMode;
40 }
41 
qdec_mcux_attr_set(const struct device * dev,enum sensor_channel ch,enum sensor_attribute attr,const struct sensor_value * val)42 static int qdec_mcux_attr_set(const struct device *dev, enum sensor_channel ch,
43 	enum sensor_attribute attr, const struct sensor_value *val)
44 {
45 	const struct qdec_mcux_config *config = dev->config;
46 	struct qdec_mcux_data *data = dev->data;
47 
48 	if (ch != SENSOR_CHAN_ROTATION) {
49 		return -ENOTSUP;
50 	}
51 
52 	switch ((enum sensor_attribute_qdec_mcux) attr) {
53 	case SENSOR_ATTR_QDEC_MOD_VAL:
54 		if (!IN_RANGE(val->val1, 1, UINT16_MAX)) {
55 			LOG_ERR("SENSOR_ATTR_QDEC_MOD_VAL value invalid");
56 			return -EINVAL;
57 		}
58 		data->counts_per_revolution = val->val1;
59 		return 0;
60 	case SENSOR_ATTR_QDEC_ENABLE_SINGLE_PHASE:
61 		data->qdec_config.decoderWorkMode =
62 			int_to_work_mode(val->val1);
63 
64 		WRITE_BIT(config->base->CTRL, ENC_CTRL_PH1_SHIFT, val->val1);
65 		return 0;
66 	default:
67 		return -ENOTSUP;
68 	}
69 }
70 
qdec_mcux_attr_get(const struct device * dev,enum sensor_channel ch,enum sensor_attribute attr,struct sensor_value * val)71 static int qdec_mcux_attr_get(const struct device *dev, enum sensor_channel ch,
72 	enum sensor_attribute attr, struct sensor_value *val)
73 {
74 	struct qdec_mcux_data *data = dev->data;
75 
76 	if (ch != SENSOR_CHAN_ROTATION) {
77 		return -ENOTSUP;
78 	}
79 
80 	switch ((enum sensor_attribute_qdec_mcux) attr) {
81 	case SENSOR_ATTR_QDEC_MOD_VAL:
82 		val->val1 = data->counts_per_revolution;
83 		return 0;
84 	case SENSOR_ATTR_QDEC_ENABLE_SINGLE_PHASE:
85 		val->val1 = data->qdec_config.decoderWorkMode ==
86 			    kENC_DecoderWorkAsNormalMode ? 0 : 1;
87 		return 0;
88 	default:
89 		return -ENOTSUP;
90 	}
91 }
92 
qdec_mcux_fetch(const struct device * dev,enum sensor_channel ch)93 static int qdec_mcux_fetch(const struct device *dev, enum sensor_channel ch)
94 {
95 	const struct qdec_mcux_config *config = dev->config;
96 	struct qdec_mcux_data *data = dev->data;
97 
98 	if (ch != SENSOR_CHAN_ALL) {
99 		return -ENOTSUP;
100 	}
101 
102 	/* Read position */
103 	data->position = ENC_GetPositionValue(config->base);
104 
105 	LOG_DBG("pos %d", data->position);
106 
107 	return 0;
108 }
109 
qdec_mcux_ch_get(const struct device * dev,enum sensor_channel ch,struct sensor_value * val)110 static int qdec_mcux_ch_get(const struct device *dev, enum sensor_channel ch,
111 			 struct sensor_value *val)
112 {
113 	struct qdec_mcux_data *data = dev->data;
114 
115 	switch (ch) {
116 	case SENSOR_CHAN_ROTATION:
117 		sensor_value_from_float(val, (data->position * 360.0f)
118 					/ data->counts_per_revolution);
119 		break;
120 	default:
121 		return -ENOTSUP;
122 	}
123 
124 	return 0;
125 }
126 
127 static DEVICE_API(sensor, qdec_mcux_api) = {
128 	.attr_set = &qdec_mcux_attr_set,
129 	.attr_get = &qdec_mcux_attr_get,
130 	.sample_fetch = &qdec_mcux_fetch,
131 	.channel_get = &qdec_mcux_ch_get,
132 };
133 
init_inputs(const struct device * dev)134 static void init_inputs(const struct device *dev)
135 {
136 	int i;
137 	const struct qdec_mcux_config *config = dev->config;
138 
139 	i = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
140 	assert(i == 0);
141 
142 	/* Quadrature Encoder inputs are only accessible via crossbar */
143 	XBARA_Init(config->xbar);
144 	for (i = 0; i < config->xbar_maps_len; i += 2) {
145 		XBARA_SetSignalsConnection(config->xbar, config->xbar_maps[i],
146 					   config->xbar_maps[i + 1]);
147 	}
148 }
149 
150 #define XBAR_PHANDLE(n)	DT_INST_PHANDLE(n, xbar)
151 
152 #define QDEC_CHECK_COND(n, p, min, max)						\
153 	COND_CODE_1(DT_INST_NODE_HAS_PROP(n, p), (				\
154 		    BUILD_ASSERT(IN_RANGE(DT_INST_PROP(n, p), min, max),	\
155 				 STRINGIFY(p) " value is out of range")), ())
156 
157 #define QDEC_SET_COND(n, v, p)							\
158 	COND_CODE_1(DT_INST_NODE_HAS_PROP(n, p), (v = DT_INST_PROP(n, p)), ())
159 
160 #define QDEC_MCUX_INIT(n)							\
161 										\
162 	BUILD_ASSERT((DT_PROP_LEN(XBAR_PHANDLE(n), xbar_maps) % 2) == 0,	\
163 			"xbar_maps length must be an even number");		\
164 	QDEC_CHECK_COND(n, counts_per_revolution, 1, UINT16_MAX);		\
165 	QDEC_CHECK_COND(n, filter_sample_period, 0, UINT8_MAX);			\
166 										\
167 	static struct qdec_mcux_data qdec_mcux_##n##_data = {			\
168 		.counts_per_revolution = DT_INST_PROP(n, counts_per_revolution) \
169 	};									\
170 										\
171 	PINCTRL_DT_INST_DEFINE(n);						\
172 										\
173 	static const struct qdec_mcux_config qdec_mcux_##n##_config = {		\
174 		.base = (ENC_Type *)DT_INST_REG_ADDR(n),			\
175 		.xbar = (XBARA_Type *)DT_REG_ADDR(XBAR_PHANDLE(n)),		\
176 		.xbar_maps_len = DT_PROP_LEN(XBAR_PHANDLE(n), xbar_maps),	\
177 		.xbar_maps = DT_PROP(XBAR_PHANDLE(n), xbar_maps),		\
178 		.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),			\
179 	};									\
180 										\
181 	static int qdec_mcux_##n##_init(const struct device *dev)		\
182 	{									\
183 		const struct qdec_mcux_config *config = dev->config;		\
184 		struct qdec_mcux_data *data = dev->data;			\
185 										\
186 		LOG_DBG("Initializing %s", dev->name);				\
187 										\
188 		init_inputs(dev);						\
189 										\
190 		ENC_GetDefaultConfig(&data->qdec_config);			\
191 		data->qdec_config.decoderWorkMode = int_to_work_mode(		\
192 			DT_INST_PROP(n, single_phase_mode));			\
193 		QDEC_SET_COND(n, data->qdec_config.filterCount, filter_count);	\
194 		QDEC_SET_COND(n, data->qdec_config.filterSamplePeriod,		\
195 			  filter_sample_period);				\
196 		LOG_DBG("Latency is %u filter clock cycles + 2 IPBus clock "	\
197 			"periods", data->qdec_config.filterSamplePeriod *	\
198 			(data->qdec_config.filterCount + 3));			\
199 		ENC_Init(config->base, &data->qdec_config);			\
200 										\
201 		/* Update the position counter with initial value. */		\
202 		ENC_DoSoftwareLoadInitialPositionValue(config->base);		\
203 										\
204 		return 0;							\
205 	}									\
206 										\
207 										\
208 	SENSOR_DEVICE_DT_INST_DEFINE(n, qdec_mcux_##n##_init, NULL,		\
209 			      &qdec_mcux_##n##_data, &qdec_mcux_##n##_config,	\
210 			      POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,		\
211 			      &qdec_mcux_api);					\
212 										\
213 
214 DT_INST_FOREACH_STATUS_OKAY(QDEC_MCUX_INIT)
215