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