1 /*
2  * Copyright (c) 2024 TDK Invensense
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "icm42670.h"
8 #include "imu/inv_imu_apex.h"
9 
icm42670_apex_enable(inv_imu_device_t * s)10 int icm42670_apex_enable(inv_imu_device_t *s)
11 {
12 	int err = 0;
13 	inv_imu_apex_parameters_t apex_inputs;
14 	inv_imu_interrupt_parameter_t config_int = {(inv_imu_interrupt_value)0};
15 
16 	/* Disabling FIFO to avoid extra power consumption due to ALP config */
17 	err |= inv_imu_configure_fifo(s, INV_IMU_FIFO_DISABLED);
18 
19 	/* Enable Pedometer, Tilt and SMD interrupts */
20 	config_int.INV_STEP_DET = INV_IMU_ENABLE;
21 	config_int.INV_STEP_CNT_OVFL = INV_IMU_ENABLE;
22 	config_int.INV_TILT_DET = INV_IMU_ENABLE;
23 	config_int.INV_SMD = INV_IMU_ENABLE;
24 	err |= inv_imu_set_config_int1(s, &config_int);
25 
26 	/* Enable accelerometer to feed the APEX Pedometer algorithm */
27 	err |= inv_imu_set_accel_frequency(s, ACCEL_CONFIG0_ODR_50_HZ);
28 
29 	/* Set 2x averaging, in order to minimize power consumption (16x by default) */
30 	err |= inv_imu_set_accel_lp_avg(s, ACCEL_CONFIG1_ACCEL_FILT_AVG_2);
31 	err |= inv_imu_enable_accel_low_power_mode(s);
32 
33 	/* Get the default parameters for the APEX features */
34 	err |= inv_imu_apex_init_parameters_struct(s, &apex_inputs);
35 
36 	/*
37 	 * Configure the power mode Normal mode.
38 	 *  Avalaible mode : Low Power mode (WoM+Pedometer),
39 	 *  configure the WoM to wake-up the DMP once it goes in power save mode
40 	 */
41 	apex_inputs.power_save = APEX_CONFIG0_DMP_POWER_SAVE_DIS;
42 	err |= inv_imu_apex_configure_parameters(s, &apex_inputs);
43 
44 	/* Configure sampling frequency to 50Hz */
45 	err |= inv_imu_apex_set_frequency(s, APEX_CONFIG1_DMP_ODR_50Hz);
46 
47 	return err;
48 }
49 
icm42670_apex_fetch_from_dmp(const struct device * dev)50 int icm42670_apex_fetch_from_dmp(const struct device *dev)
51 {
52 	struct icm42670_data *data = dev->data;
53 	int rc = 0;
54 	uint8_t int_status2, int_status3;
55 
56 	/* Read APEX interrupt status */
57 	rc |= inv_imu_read_reg(&data->driver, INT_STATUS2, 1, &int_status2);
58 	rc |= inv_imu_read_reg(&data->driver, INT_STATUS3, 1, &int_status3);
59 
60 	/* Test Pedometer interrupt */
61 	if (int_status3 & (INT_STATUS3_STEP_DET_INT_MASK)) {
62 		inv_imu_apex_step_activity_t apex_pedometer;
63 		uint8_t step_cnt_ovflw = 0;
64 
65 		if (int_status3 & INT_STATUS3_STEP_CNT_OVF_INT_MASK) {
66 			step_cnt_ovflw = 1;
67 		}
68 
69 		rc |= inv_imu_apex_get_data_activity(&data->driver, &apex_pedometer);
70 
71 		if (data->pedometer_cnt !=
72 		    apex_pedometer.step_cnt + step_cnt_ovflw * (uint64_t)UINT16_MAX) {
73 			data->pedometer_cnt =
74 				apex_pedometer.step_cnt + step_cnt_ovflw * (uint64_t)UINT16_MAX;
75 			data->pedometer_activity = apex_pedometer.activity_class;
76 			data->pedometer_cadence = apex_pedometer.step_cadence;
77 		} else {
78 			/* Pedometer data processing */
79 			rc = 1;
80 		}
81 	}
82 	/* Test Tilt interrupt */
83 	if (int_status3 & (INT_STATUS3_TILT_DET_INT_MASK)) {
84 		data->apex_status = ICM42670_APEX_STATUS_MASK_TILT;
85 	}
86 	/* Test SMD interrupt */
87 	if ((int_status2 & (INT_STATUS2_SMD_INT_MASK)) || (rc != 0)) {
88 		data->apex_status = ICM42670_APEX_STATUS_MASK_SMD;
89 	}
90 	/* Test WOM interrupts */
91 	if (int_status2 & (INT_STATUS2_WOM_X_INT_MASK | INT_STATUS2_WOM_Y_INT_MASK |
92 			   INT_STATUS2_WOM_Z_INT_MASK)) {
93 		data->apex_status = 0;
94 		if (int_status2 & INT_STATUS2_WOM_X_INT_MASK) {
95 			data->apex_status |= ICM42670_APEX_STATUS_MASK_WOM_X;
96 		}
97 		if (int_status2 & INT_STATUS2_WOM_Y_INT_MASK) {
98 			data->apex_status |= ICM42670_APEX_STATUS_MASK_WOM_Y;
99 		}
100 		if (int_status2 & INT_STATUS2_WOM_Z_INT_MASK) {
101 			data->apex_status |= ICM42670_APEX_STATUS_MASK_WOM_Z;
102 		}
103 	}
104 	return rc;
105 }
106 
icm42670_apex_pedometer_cadence_convert(struct sensor_value * val,uint8_t raw_val,uint8_t dmp_odr_hz)107 void icm42670_apex_pedometer_cadence_convert(struct sensor_value *val, uint8_t raw_val,
108 					     uint8_t dmp_odr_hz)
109 {
110 	int64_t conv_val;
111 
112 	/* Converting u6.2 */
113 	conv_val = (int64_t)(dmp_odr_hz << 2) * 1000000 / (raw_val + (raw_val & 0x03));
114 	val->val1 = conv_val / 1000000;
115 	val->val2 = conv_val % 1000000;
116 }
117 
icm42670_apex_enable_pedometer(const struct device * dev,inv_imu_device_t * s)118 int icm42670_apex_enable_pedometer(const struct device *dev, inv_imu_device_t *s)
119 {
120 	struct icm42670_data *data = dev->data;
121 
122 	data->dmp_odr_hz = 50;
123 	/* Enable the pedometer */
124 	return inv_imu_apex_enable_pedometer(s);
125 }
126 
icm42670_apex_enable_tilt(inv_imu_device_t * s)127 int icm42670_apex_enable_tilt(inv_imu_device_t *s)
128 {
129 	/* Enable Tilt */
130 	return inv_imu_apex_enable_tilt(s);
131 }
132 
icm42670_apex_enable_smd(inv_imu_device_t * s)133 int icm42670_apex_enable_smd(inv_imu_device_t *s)
134 {
135 	int rc = 0;
136 
137 	/* Enable SMD (and Pedometer as SMD uses it) */
138 	rc |= inv_imu_apex_enable_pedometer(s);
139 	rc |= inv_imu_apex_enable_smd(s);
140 
141 	return rc;
142 }
143 
icm42670_apex_enable_wom(inv_imu_device_t * s)144 int icm42670_apex_enable_wom(inv_imu_device_t *s)
145 {
146 	int rc = 0;
147 	inv_imu_interrupt_parameter_t config_int = {(inv_imu_interrupt_value)0};
148 
149 	/*
150 	 * Optimize power consumption:
151 	 * - Disable FIFO usage.
152 	 * - Disable data ready interrupt and enable WOM interrupts.
153 	 * - Set 2X averaging.
154 	 * - Use Low-Power mode at low frequency.
155 	 */
156 	rc |= inv_imu_configure_fifo(s, INV_IMU_FIFO_DISABLED);
157 
158 	config_int.INV_WOM_X = INV_IMU_ENABLE;
159 	config_int.INV_WOM_Y = INV_IMU_ENABLE;
160 	config_int.INV_WOM_Z = INV_IMU_ENABLE;
161 	rc |= inv_imu_set_config_int1(s, &config_int);
162 
163 	rc |= inv_imu_set_accel_lp_avg(s, ACCEL_CONFIG1_ACCEL_FILT_AVG_2);
164 	rc |= inv_imu_set_accel_frequency(s, ACCEL_CONFIG0_ODR_12_5_HZ);
165 	rc |= inv_imu_enable_accel_low_power_mode(s);
166 
167 	/*
168 	 * Configure WOM thresholds for each axis to 195 mg (Resolution 1g/256)
169 	 * WOM threshold = 50 * 1000 / 256 = 195 mg
170 	 * and enable WOM
171 	 */
172 	rc |= inv_imu_configure_wom(s, 50, 50, 50, WOM_CONFIG_WOM_INT_MODE_ORED,
173 				    WOM_CONFIG_WOM_INT_DUR_1_SMPL);
174 	rc |= inv_imu_enable_wom(s);
175 
176 	return rc;
177 }
178