1 /*
2  * SPDX-License-Identifier: Apache-2.0
3  *
4  * Copyright (c) 2023 Linumiz
5  */
6 
7 #define DT_DRV_COMPAT memsic_mc3419
8 
9 #include <zephyr/init.h>
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/sys/byteorder.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/logging/log.h>
14 #include "mc3419.h"
15 
16 LOG_MODULE_REGISTER(MC3419, CONFIG_SENSOR_LOG_LEVEL);
17 
18 static const uint16_t mc3419_accel_sense_map[] = {1, 2, 4, 8, 6};
19 static struct mc3419_odr_map odr_map_table[] = {
20 	{25}, {50}, {62, 500}, {100},
21 	{125}, {250}, {500}, {1000}
22 };
23 
mc3419_get_odr_value(uint16_t freq,uint16_t m_freq)24 static int mc3419_get_odr_value(uint16_t freq, uint16_t m_freq)
25 {
26 	for (int i = 0; i < ARRAY_SIZE(odr_map_table); i++) {
27 		if (odr_map_table[i].freq == freq &&
28 		    odr_map_table[i].mfreq == m_freq) {
29 			return i;
30 		}
31 	}
32 
33 	return -EINVAL;
34 }
35 
mc3419_set_op_mode(const struct mc3419_config * cfg,enum mc3419_op_mode mode)36 static inline int mc3419_set_op_mode(const struct mc3419_config *cfg,
37 				     enum mc3419_op_mode mode)
38 {
39 	return i2c_reg_write_byte_dt(&cfg->i2c, MC3419_REG_OP_MODE, mode);
40 }
41 
mc3419_sample_fetch(const struct device * dev,enum sensor_channel chan)42 static int mc3419_sample_fetch(const struct device *dev,
43 			       enum sensor_channel chan)
44 {
45 	int ret = 0;
46 	const struct mc3419_config *cfg = dev->config;
47 	struct mc3419_driver_data *data = dev->data;
48 
49 	k_sem_take(&data->sem, K_FOREVER);
50 	ret = i2c_burst_read_dt(&cfg->i2c, MC3419_REG_XOUT_L,
51 				(uint8_t *)data->samples,
52 				MC3419_SAMPLE_READ_SIZE);
53 	k_sem_give(&data->sem);
54 	return ret;
55 }
56 
mc3419_to_sensor_value(double sensitivity,int16_t * raw_data,struct sensor_value * val)57 static int mc3419_to_sensor_value(double sensitivity, int16_t *raw_data,
58 				  struct sensor_value *val)
59 {
60 	double value = sys_le16_to_cpu(*raw_data);
61 
62 	value *= sensitivity * SENSOR_GRAVITY_DOUBLE / 1000;
63 
64 	return sensor_value_from_double(val, value);
65 }
66 
mc3419_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)67 static int mc3419_channel_get(const struct device *dev,
68 			      enum sensor_channel chan,
69 			      struct sensor_value *val)
70 {
71 	int ret = 0;
72 	struct mc3419_driver_data *data = dev->data;
73 
74 	k_sem_take(&data->sem, K_FOREVER);
75 	switch (chan) {
76 	case SENSOR_CHAN_ACCEL_X:
77 		ret = mc3419_to_sensor_value(data->sensitivity, &data->samples[0], val);
78 		break;
79 	case SENSOR_CHAN_ACCEL_Y:
80 		ret = mc3419_to_sensor_value(data->sensitivity, &data->samples[1], val);
81 		break;
82 	case SENSOR_CHAN_ACCEL_Z:
83 		ret = mc3419_to_sensor_value(data->sensitivity, &data->samples[2], val);
84 		break;
85 	case SENSOR_CHAN_ACCEL_XYZ:
86 		ret = mc3419_to_sensor_value(data->sensitivity, &data->samples[0], &val[0]);
87 		ret |= mc3419_to_sensor_value(data->sensitivity, &data->samples[1], &val[1]);
88 		ret |= mc3419_to_sensor_value(data->sensitivity, &data->samples[2], &val[2]);
89 		break;
90 	default:
91 		LOG_ERR("Unsupported channel");
92 		ret = -ENOTSUP;
93 	}
94 
95 	k_sem_give(&data->sem);
96 	return ret;
97 }
98 
mc3419_set_accel_range(const struct device * dev,uint8_t range)99 static int mc3419_set_accel_range(const struct device *dev, uint8_t range)
100 {
101 	int ret = 0;
102 	const struct mc3419_config *cfg = dev->config;
103 	struct mc3419_driver_data *data = dev->data;
104 
105 	if (range >= MC3419_ACCL_RANGE_END) {
106 		LOG_ERR("Accel resolution is out of range");
107 		return -EINVAL;
108 	}
109 
110 	ret = i2c_reg_update_byte_dt(&cfg->i2c, MC3419_REG_RANGE_SELECT_CTRL,
111 				     MC3419_RANGE_MASK, range << 4);
112 	if (ret < 0) {
113 		LOG_ERR("Failed to set resolution (%d)", ret);
114 		return ret;
115 	}
116 
117 	data->sensitivity = (double)(mc3419_accel_sense_map[range] *
118 				     SENSOR_GRAIN_VALUE);
119 
120 	return 0;
121 }
122 
mc3419_set_odr(const struct device * dev,const struct sensor_value * val)123 static int mc3419_set_odr(const struct device *dev,
124 			  const struct sensor_value *val)
125 {
126 	int ret = 0;
127 	int data_rate = 0;
128 	const struct mc3419_config *cfg = dev->config;
129 
130 	ret = mc3419_get_odr_value(val->val1, val->val2);
131 	if (ret < 0) {
132 		LOG_ERR("Failed to get odr value from odr map (%d)", ret);
133 		return ret;
134 	}
135 
136 	data_rate = MC3419_BASE_ODR_VAL + ret;
137 
138 	ret = i2c_reg_write_byte_dt(&cfg->i2c, MC3419_REG_SAMPLE_RATE,
139 				    data_rate);
140 	if (ret < 0) {
141 		LOG_ERR("Failed to set ODR (%d)", ret);
142 		return ret;
143 	}
144 
145 	LOG_DBG("Set ODR Rate to 0x%x", data_rate);
146 	ret = i2c_reg_write_byte_dt(&cfg->i2c, MC3419_REG_SAMPLE_RATE_2, cfg->decimation_rate);
147 	if (ret < 0) {
148 		LOG_ERR("Failed to set decimation rate (%d)", ret);
149 		return ret;
150 	}
151 
152 	return 0;
153 }
154 
155 #if defined(CONFIG_MC3419_TRIGGER)
mc3419_set_anymotion_threshold(const struct device * dev,const struct sensor_value * val)156 static int mc3419_set_anymotion_threshold(const struct device *dev,
157 					  const struct sensor_value *val)
158 {
159 	int ret = 0;
160 	const struct mc3419_config *cfg = dev->config;
161 	uint8_t buf[3] = {0};
162 
163 	if (val->val1 > MC3419_ANY_MOTION_THRESH_MAX) {
164 		return -EINVAL;
165 	}
166 
167 	buf[0] = MC3419_REG_ANY_MOTION_THRES;
168 	sys_put_le16((uint16_t)val->val1, &buf[1]);
169 
170 	ret = i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
171 	if (ret < 0) {
172 		LOG_ERR("Failed to set anymotion threshold (%d)", ret);
173 		return ret;
174 	}
175 
176 	return 0;
177 }
178 
mc3419_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)179 static int mc3419_trigger_set(const struct device *dev,
180 			      const struct sensor_trigger *trig,
181 			      sensor_trigger_handler_t handler)
182 {
183 	int ret = 0;
184 	const struct mc3419_config *cfg = dev->config;
185 	struct mc3419_driver_data *data = dev->data;
186 
187 	k_sem_take(&data->sem, K_FOREVER);
188 	ret = mc3419_set_op_mode(cfg, MC3419_MODE_STANDBY);
189 	if (ret < 0) {
190 		goto exit;
191 	}
192 
193 	ret = mc3419_configure_trigger(dev, trig, handler);
194 	if (ret < 0) {
195 		LOG_ERR("Failed to set trigger (%d)", ret);
196 	}
197 
198 exit:
199 	mc3419_set_op_mode(cfg, MC3419_MODE_WAKE);
200 
201 	k_sem_give(&data->sem);
202 	return ret;
203 }
204 #endif
205 
mc3419_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)206 static int mc3419_attr_set(const struct device *dev,
207 			   enum sensor_channel chan,
208 			   enum sensor_attribute attr,
209 			   const struct sensor_value *val)
210 {
211 	int ret = 0;
212 	struct mc3419_driver_data *data = dev->data;
213 
214 	if (chan != SENSOR_CHAN_ACCEL_X &&
215 	    chan != SENSOR_CHAN_ACCEL_Y &&
216 	    chan != SENSOR_CHAN_ACCEL_Z &&
217 	    chan != SENSOR_CHAN_ACCEL_XYZ) {
218 		LOG_ERR("Not supported on this channel.");
219 		return -ENOTSUP;
220 	}
221 
222 	k_sem_take(&data->sem, K_FOREVER);
223 	ret = mc3419_set_op_mode(dev->config, MC3419_MODE_STANDBY);
224 	if (ret < 0) {
225 		goto exit;
226 	}
227 
228 	switch (attr) {
229 	case SENSOR_ATTR_FULL_SCALE:
230 		ret = mc3419_set_accel_range(dev, val->val1);
231 		break;
232 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
233 		ret = mc3419_set_odr(dev, val);
234 		break;
235 #if defined(CONFIG_MC3419_TRIGGER)
236 	case SENSOR_ATTR_SLOPE_TH:
237 		ret = mc3419_set_anymotion_threshold(dev, val);
238 		break;
239 #endif
240 	default:
241 		LOG_ERR("ACCEL attribute is not supported");
242 		ret = -EINVAL;
243 	}
244 
245 exit:
246 	mc3419_set_op_mode(dev->config, MC3419_MODE_WAKE);
247 
248 	k_sem_give(&data->sem);
249 	return ret;
250 }
251 
mc3419_init(const struct device * dev)252 static int mc3419_init(const struct device *dev)
253 {
254 	int ret = 0;
255 	struct mc3419_driver_data *data = dev->data;
256 	const struct mc3419_config *cfg = dev->config;
257 
258 	if (!(i2c_is_ready_dt(&cfg->i2c))) {
259 		LOG_ERR("Bus device is not ready");
260 		return -ENODEV;
261 	}
262 
263 	k_sem_init(&data->sem, 1, 1);
264 
265 #if defined(CONFIG_MC3419_TRIGGER)
266 	ret = mc3419_trigger_init(dev);
267 	if (ret < 0) {
268 		LOG_ERR("Could not initialize interrupts");
269 		return ret;
270 	}
271 #endif
272 
273 	ret = i2c_reg_update_byte_dt(&cfg->i2c, MC3419_REG_RANGE_SELECT_CTRL, MC3419_LPF_MASK,
274 				     cfg->lpf_fc_sel);
275 	if (ret < 0) {
276 		LOG_ERR("Failed to configure LPF (%d)", ret);
277 		return ret;
278 	}
279 	/* Leave the sensor in default power on state, will be
280 	 * enabled by configure attr or setting trigger.
281 	 */
282 
283 	LOG_INF("MC3419 Initialized");
284 
285 	return ret;
286 }
287 
288 static DEVICE_API(sensor, mc3419_api) = {
289 	.attr_set = mc3419_attr_set,
290 #if defined(CONFIG_MC3419_TRIGGER)
291 	.trigger_set = mc3419_trigger_set,
292 #endif
293 	.sample_fetch = mc3419_sample_fetch,
294 	.channel_get = mc3419_channel_get,
295 };
296 
297 #if defined(CONFIG_MC3419_TRIGGER)
298 #define MC3419_CFG_IRQ(idx)						\
299 	.int_gpio = GPIO_DT_SPEC_INST_GET_OR(idx, int_gpios, { 0 }),	\
300 	.int_cfg  = DT_INST_PROP(idx, int_pin2),
301 #else
302 #define MC3419_CFG_IRQ(idx)
303 #endif
304 
305 #define MC3419_DEFINE(idx)                                                                         \
306                                                                                                    \
307 	static const struct mc3419_config mc3419_config_##idx = {                                  \
308 		.i2c = I2C_DT_SPEC_INST_GET(idx),                                                  \
309 		.lpf_fc_sel = DT_INST_PROP(idx, lpf_fc_sel),                                       \
310 		.decimation_rate = DT_INST_PROP(idx, decimation_rate),                             \
311 		MC3419_CFG_IRQ(idx)};                                                              \
312 	static struct mc3419_driver_data mc3419_data_##idx;                                        \
313 	SENSOR_DEVICE_DT_INST_DEFINE(idx, mc3419_init, NULL, &mc3419_data_##idx,                   \
314 				     &mc3419_config_##idx, POST_KERNEL,                            \
315 				     CONFIG_SENSOR_INIT_PRIORITY, &mc3419_api);
316 
317 DT_INST_FOREACH_STATUS_OKAY(MC3419_DEFINE)
318