1 /* ST Microelectronics IIS2DH 3-axis accelerometer driver
2  *
3  * Copyright (c) 2020 STMicroelectronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  * https://www.st.com/resource/en/datasheet/iis2dh.pdf
9  */
10 
11 #define DT_DRV_COMPAT st_iis2dh
12 
13 #include <zephyr/init.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/sys/util_macro.h>
17 #include <zephyr/logging/log.h>
18 #include <zephyr/drivers/sensor.h>
19 
20 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
21 #include <zephyr/drivers/spi.h>
22 #elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
23 #include <zephyr/drivers/i2c.h>
24 #endif
25 
26 #include "iis2dh.h"
27 
28 LOG_MODULE_REGISTER(IIS2DH, CONFIG_SENSOR_LOG_LEVEL);
29 
30 /* gains in uG/LSB */
31 static const uint32_t iis2dh_gain[3][4] = {
32 	{
33 		/* HR mode */
34 		980/16,		/* 2G */
35 		1950/16,	/* 4G */
36 		3910/16,	/* 8G */
37 		11720/16,	/* 16G */
38 	},
39 	{
40 		/* NM mode */
41 		3910/64,	/* 2G */
42 		7810/64,	/* 4G */
43 		15630/64,	/* 8G */
44 		46950/64,	/* 16G */
45 	},
46 	{
47 		/* LP mode */
48 		15630/256,	/* 2G */
49 		31250/256,	/* 4G */
50 		62500/256,	/* 8G */
51 		188680/256,	/* 16G */
52 	},
53 };
54 
iis2dh_set_fs_raw(const struct device * dev,uint8_t fs)55 static int iis2dh_set_fs_raw(const struct device *dev, uint8_t fs)
56 {
57 	struct iis2dh_data *iis2dh = dev->data;
58 	int err;
59 
60 	err = iis2dh_full_scale_set(iis2dh->ctx, fs);
61 
62 	if (!err) {
63 		/* save internally gain for optimization */
64 		iis2dh->gain = iis2dh_gain[IIS2DH_HR_12bit][fs];
65 	}
66 
67 	return err;
68 }
69 
70 #if (CONFIG_IIS2DH_RANGE == 0)
71 /**
72  * iis2dh_set_range - set full scale range for acc
73  * @dev: Pointer to instance of struct device (I2C or SPI)
74  * @range: Full scale range (2, 4, 8 and 16 G)
75  */
iis2dh_set_range(const struct device * dev,uint16_t range)76 static int iis2dh_set_range(const struct device *dev, uint16_t range)
77 {
78 	int err;
79 	uint8_t fs = IIS2DH_FS_TO_REG(range);
80 
81 	err = iis2dh_set_fs_raw(dev, fs);
82 
83 	return err;
84 }
85 #endif
86 
87 #if (CONFIG_IIS2DH_ODR == 0)
88 /**
89  * iis2dh_set_odr - set new sampling frequency
90  * @dev: Pointer to instance of struct device (I2C or SPI)
91  * @odr: Output data rate
92  */
iis2dh_set_odr(const struct device * dev,uint16_t odr)93 static int iis2dh_set_odr(const struct device *dev, uint16_t odr)
94 {
95 	struct iis2dh_data *iis2dh = dev->data;
96 	const struct iis2dh_device_config *cfg = dev->config;
97 	iis2dh_odr_t val;
98 
99 	val = IIS2DH_ODR_TO_REG_HR(cfg->pm, odr);
100 
101 	return iis2dh_data_rate_set(iis2dh->ctx, val);
102 }
103 #endif
104 
iis2dh_convert(struct sensor_value * val,int raw_val,uint32_t gain)105 static inline void iis2dh_convert(struct sensor_value *val, int raw_val,
106 				  uint32_t gain)
107 {
108 	int64_t dval;
109 
110 	/* Gain is in ug/LSB */
111 	/* Convert to m/s^2 */
112 	dval = ((int64_t)raw_val * gain * SENSOR_G) / 1000000LL;
113 	val->val1 = dval / 1000000LL;
114 	val->val2 = dval % 1000000LL;
115 }
116 
iis2dh_channel_get_acc(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)117 static inline void iis2dh_channel_get_acc(const struct device *dev,
118 					  enum sensor_channel chan,
119 					  struct sensor_value *val)
120 {
121 	int i;
122 	uint8_t ofs_start, ofs_stop;
123 	struct iis2dh_data *iis2dh = dev->data;
124 	struct sensor_value *pval = val;
125 
126 	switch (chan) {
127 	case SENSOR_CHAN_ACCEL_X:
128 		ofs_start = ofs_stop = 0U;
129 		break;
130 	case SENSOR_CHAN_ACCEL_Y:
131 		ofs_start = ofs_stop = 1U;
132 		break;
133 	case SENSOR_CHAN_ACCEL_Z:
134 		ofs_start = ofs_stop = 2U;
135 		break;
136 	default:
137 		ofs_start = 0U; ofs_stop = 2U;
138 		break;
139 	}
140 
141 	for (i = ofs_start; i <= ofs_stop ; i++) {
142 		iis2dh_convert(pval++, iis2dh->acc[i], iis2dh->gain);
143 	}
144 }
145 
iis2dh_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)146 static int iis2dh_channel_get(const struct device *dev,
147 			      enum sensor_channel chan,
148 			      struct sensor_value *val)
149 {
150 	switch (chan) {
151 	case SENSOR_CHAN_ACCEL_X:
152 	case SENSOR_CHAN_ACCEL_Y:
153 	case SENSOR_CHAN_ACCEL_Z:
154 	case SENSOR_CHAN_ACCEL_XYZ:
155 		iis2dh_channel_get_acc(dev, chan, val);
156 		return 0;
157 	default:
158 		LOG_DBG("Channel not supported");
159 		break;
160 	}
161 
162 	return -ENOTSUP;
163 }
164 
iis2dh_config(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)165 static int iis2dh_config(const struct device *dev, enum sensor_channel chan,
166 			 enum sensor_attribute attr,
167 			 const struct sensor_value *val)
168 {
169 	switch (attr) {
170 #if (CONFIG_IIS2DH_RANGE == 0)
171 	case SENSOR_ATTR_FULL_SCALE:
172 		return iis2dh_set_range(dev, sensor_ms2_to_g(val));
173 #endif
174 #if (CONFIG_IIS2DH_ODR == 0)
175 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
176 		return iis2dh_set_odr(dev, val->val1);
177 #endif
178 	default:
179 		LOG_DBG("Acc attribute not supported");
180 		break;
181 	}
182 
183 	return -ENOTSUP;
184 }
185 
iis2dh_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)186 static int iis2dh_attr_set(const struct device *dev, enum sensor_channel chan,
187 			   enum sensor_attribute attr,
188 			   const struct sensor_value *val)
189 {
190 	switch (chan) {
191 	case SENSOR_CHAN_ACCEL_X:
192 	case SENSOR_CHAN_ACCEL_Y:
193 	case SENSOR_CHAN_ACCEL_Z:
194 	case SENSOR_CHAN_ACCEL_XYZ:
195 		return iis2dh_config(dev, chan, attr, val);
196 	default:
197 		LOG_DBG("Attr not supported on %d channel", chan);
198 		break;
199 	}
200 
201 	return -ENOTSUP;
202 }
203 
iis2dh_sample_fetch(const struct device * dev,enum sensor_channel chan)204 static int iis2dh_sample_fetch(const struct device *dev,
205 			       enum sensor_channel chan)
206 {
207 	struct iis2dh_data *iis2dh = dev->data;
208 	int16_t buf[3];
209 
210 	/* fetch raw data sample */
211 	if (iis2dh_acceleration_raw_get(iis2dh->ctx, buf) < 0) {
212 		LOG_DBG("Failed to fetch raw data sample");
213 		return -EIO;
214 	}
215 
216 	iis2dh->acc[0] = sys_le16_to_cpu(buf[0]);
217 	iis2dh->acc[1] = sys_le16_to_cpu(buf[1]);
218 	iis2dh->acc[2] = sys_le16_to_cpu(buf[2]);
219 
220 	return 0;
221 }
222 
223 static const struct sensor_driver_api iis2dh_driver_api = {
224 	.attr_set = iis2dh_attr_set,
225 #if CONFIG_IIS2DH_TRIGGER
226 	.trigger_set = iis2dh_trigger_set,
227 #endif /* CONFIG_IIS2DH_TRIGGER */
228 	.sample_fetch = iis2dh_sample_fetch,
229 	.channel_get = iis2dh_channel_get,
230 };
231 
iis2dh_init_interface(const struct device * dev)232 static int iis2dh_init_interface(const struct device *dev)
233 {
234 	int res;
235 
236 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
237 	res = iis2dh_spi_init(dev);
238 	if (res) {
239 		return res;
240 	}
241 #elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
242 	res = iis2dh_i2c_init(dev);
243 	if (res) {
244 		return res;
245 	}
246 #else
247 #error "BUS MACRO NOT DEFINED IN DTS"
248 #endif
249 
250 	return 0;
251 }
252 
iis2dh_init(const struct device * dev)253 static int iis2dh_init(const struct device *dev)
254 {
255 	struct iis2dh_data *iis2dh = dev->data;
256 	const struct iis2dh_device_config *cfg = dev->config;
257 	uint8_t wai;
258 
259 	if (iis2dh_init_interface(dev)) {
260 		return -EINVAL;
261 	}
262 
263 	/* check chip ID */
264 	if (iis2dh_device_id_get(iis2dh->ctx, &wai) < 0) {
265 		return -EIO;
266 	}
267 
268 	if (wai != IIS2DH_ID) {
269 		LOG_ERR("Invalid chip ID: %02x", wai);
270 		return -EINVAL;
271 	}
272 
273 	if (iis2dh_block_data_update_set(iis2dh->ctx, PROPERTY_ENABLE) < 0) {
274 		return -EIO;
275 	}
276 
277 	if (iis2dh_operating_mode_set(iis2dh->ctx, cfg->pm)) {
278 		return -EIO;
279 	}
280 
281 #if (CONFIG_IIS2DH_ODR != 0)
282 	/* set default odr and full scale for acc */
283 	if (iis2dh_data_rate_set(iis2dh->ctx, CONFIG_IIS2DH_ODR) < 0) {
284 		return -EIO;
285 	}
286 #endif
287 
288 #if (CONFIG_IIS2DH_RANGE != 0)
289 	iis2dh_set_fs_raw(dev, CONFIG_IIS2DH_RANGE);
290 #endif
291 
292 #ifdef CONFIG_IIS2DH_TRIGGER
293 	if (cfg->int_gpio.port) {
294 		if (iis2dh_init_interrupt(dev) < 0) {
295 			LOG_ERR("Failed to initialize interrupts");
296 			return -EIO;
297 		}
298 	}
299 #endif /* CONFIG_IIS2DH_TRIGGER */
300 
301 	return 0;
302 }
303 
304 #define IIS2DH_SPI(inst)                                                                           \
305 	(.spi = SPI_DT_SPEC_INST_GET(                                                              \
306 		 0, SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8), 0),)
307 
308 #define IIS2DH_I2C(inst) (.i2c = I2C_DT_SPEC_INST_GET(inst),)
309 
310 #define IIS2DH_DEFINE(inst)									\
311 	static struct iis2dh_data iis2dh_data_##inst;						\
312 												\
313 	static const struct iis2dh_device_config iis2dh_device_config_##inst = {		\
314 		COND_CODE_1(DT_INST_ON_BUS(inst, i2c), IIS2DH_I2C(inst), ())			\
315 		COND_CODE_1(DT_INST_ON_BUS(inst, spi), IIS2DH_SPI(inst), ())			\
316 		.pm = CONFIG_IIS2DH_POWER_MODE,							\
317 		IF_ENABLED(CONFIG_IIS2DH_TRIGGER,						\
318 			   (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, drdy_gpios, { 0 }),))	\
319 	};											\
320 												\
321 	SENSOR_DEVICE_DT_INST_DEFINE(inst, iis2dh_init, NULL,					\
322 			      &iis2dh_data_##inst, &iis2dh_device_config_##inst, POST_KERNEL,	\
323 			      CONFIG_SENSOR_INIT_PRIORITY, &iis2dh_driver_api);			\
324 
325 DT_INST_FOREACH_STATUS_OKAY(IIS2DH_DEFINE)
326