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,
147 				    CONFIG_MC3419_DECIMATION_RATE);
148 	if (ret < 0) {
149 		LOG_ERR("Failed to set decimation rate (%d)", ret);
150 		return ret;
151 	}
152 
153 	return 0;
154 }
155 
156 #if defined(CONFIG_MC3419_TRIGGER)
mc3419_set_anymotion_threshold(const struct device * dev,const struct sensor_value * val)157 static int mc3419_set_anymotion_threshold(const struct device *dev,
158 					  const struct sensor_value *val)
159 {
160 	int ret = 0;
161 	const struct mc3419_config *cfg = dev->config;
162 	uint8_t buf[3] = {0};
163 
164 	if (val->val1 > MC3419_ANY_MOTION_THRESH_MAX) {
165 		return -EINVAL;
166 	}
167 
168 	buf[0] = MC3419_REG_ANY_MOTION_THRES;
169 	sys_put_le16((uint16_t)val->val1, &buf[1]);
170 
171 	ret = i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
172 	if (ret < 0) {
173 		LOG_ERR("Failed to set anymotion threshold (%d)", ret);
174 		return ret;
175 	}
176 
177 	return 0;
178 }
179 
mc3419_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)180 static int mc3419_trigger_set(const struct device *dev,
181 			      const struct sensor_trigger *trig,
182 			      sensor_trigger_handler_t handler)
183 {
184 	int ret = 0;
185 	const struct mc3419_config *cfg = dev->config;
186 	struct mc3419_driver_data *data = dev->data;
187 
188 	k_sem_take(&data->sem, K_FOREVER);
189 	ret = mc3419_set_op_mode(cfg, MC3419_MODE_STANDBY);
190 	if (ret < 0) {
191 		goto exit;
192 	}
193 
194 	ret = mc3419_configure_trigger(dev, trig, handler);
195 	if (ret < 0) {
196 		LOG_ERR("Failed to set trigger (%d)", ret);
197 	}
198 
199 exit:
200 	mc3419_set_op_mode(cfg, MC3419_MODE_WAKE);
201 
202 	k_sem_give(&data->sem);
203 	return ret;
204 }
205 #endif
206 
mc3419_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)207 static int mc3419_attr_set(const struct device *dev,
208 			   enum sensor_channel chan,
209 			   enum sensor_attribute attr,
210 			   const struct sensor_value *val)
211 {
212 	int ret = 0;
213 	struct mc3419_driver_data *data = dev->data;
214 
215 	if (chan != SENSOR_CHAN_ACCEL_X &&
216 	    chan != SENSOR_CHAN_ACCEL_Y &&
217 	    chan != SENSOR_CHAN_ACCEL_Z &&
218 	    chan != SENSOR_CHAN_ACCEL_XYZ) {
219 		LOG_ERR("Not supported on this channel.");
220 		return -ENOTSUP;
221 	}
222 
223 	k_sem_take(&data->sem, K_FOREVER);
224 	ret = mc3419_set_op_mode(dev->config, MC3419_MODE_STANDBY);
225 	if (ret < 0) {
226 		goto exit;
227 	}
228 
229 	switch (attr) {
230 	case SENSOR_ATTR_FULL_SCALE:
231 		ret = mc3419_set_accel_range(dev, val->val1);
232 		break;
233 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
234 		ret = mc3419_set_odr(dev, val);
235 		break;
236 #if defined(CONFIG_MC3419_TRIGGER)
237 	case SENSOR_ATTR_SLOPE_TH:
238 		ret = mc3419_set_anymotion_threshold(dev, val);
239 		break;
240 #endif
241 	default:
242 		LOG_ERR("ACCEL attribute is not supported");
243 		ret = -EINVAL;
244 	}
245 
246 exit:
247 	mc3419_set_op_mode(dev->config, MC3419_MODE_WAKE);
248 
249 	k_sem_give(&data->sem);
250 	return ret;
251 }
252 
mc3419_init(const struct device * dev)253 static int mc3419_init(const struct device *dev)
254 {
255 	int ret = 0;
256 	struct mc3419_driver_data *data = dev->data;
257 	const struct mc3419_config *cfg = dev->config;
258 
259 	if (!(i2c_is_ready_dt(&cfg->i2c))) {
260 		LOG_ERR("Bus device is not ready");
261 		return -ENODEV;
262 	}
263 
264 	k_sem_init(&data->sem, 1, 1);
265 
266 #if defined(CONFIG_MC3419_TRIGGER)
267 	ret = mc3419_trigger_init(dev);
268 	if (ret < 0) {
269 		LOG_ERR("Could not initialize interrupts");
270 		return ret;
271 	}
272 #endif
273 
274 	/* Leave the sensor in default power on state, will be
275 	 * enabled by configure attr or setting trigger.
276 	 */
277 
278 	LOG_INF("MC3419 Initialized");
279 
280 	return ret;
281 }
282 
283 static const struct sensor_driver_api mc3419_api = {
284 	.attr_set = mc3419_attr_set,
285 #if defined(CONFIG_MC3419_TRIGGER)
286 	.trigger_set = mc3419_trigger_set,
287 #endif
288 	.sample_fetch = mc3419_sample_fetch,
289 	.channel_get = mc3419_channel_get,
290 };
291 
292 #if defined(CONFIG_MC3419_TRIGGER)
293 #define MC3419_CFG_IRQ(idx)						\
294 	.int_gpio = GPIO_DT_SPEC_INST_GET_OR(idx, int_gpios, { 0 }),	\
295 	.int_cfg  = DT_INST_PROP(idx, int_pin2),
296 #else
297 #define MC3419_CFG_IRQ(idx)
298 #endif
299 
300 #define MC3419_DEFINE(idx)						\
301 	static const struct mc3419_config mc3419_config_##idx = {	\
302 		.i2c = I2C_DT_SPEC_INST_GET(idx),			\
303 		MC3419_CFG_IRQ(idx)					\
304 	};								\
305 	static struct mc3419_driver_data mc3419_data_##idx;		\
306 	SENSOR_DEVICE_DT_INST_DEFINE(idx,				\
307 				mc3419_init, NULL,			\
308 				&mc3419_data_##idx,			\
309 				&mc3419_config_##idx,			\
310 				POST_KERNEL,				\
311 				CONFIG_SENSOR_INIT_PRIORITY,		\
312 				&mc3419_api);
313 
314 DT_INST_FOREACH_STATUS_OKAY(MC3419_DEFINE)
315