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