1 /*
2  * Copyright (c) 2023 Google LLC
3  * Copyright (c) 2024 Florian Weber <Florian.Weber@live.de>
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT asahi_kasei_akm09918c
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/drivers/sensor_data_types.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/sys/util.h>
17 #include <zephyr/logging/log.h>
18 
19 #include "akm09918c.h"
20 #include "akm09918c_reg.h"
21 
22 LOG_MODULE_REGISTER(AKM09918C, CONFIG_SENSOR_LOG_LEVEL);
23 
24 /**
25  * @brief Perform the bus transaction to start measurement.
26  *
27  * @param dev Sensor device to operate on
28  * @param chan Channel ID for starting the measurement
29  * @return int 0 if successful or error code
30  */
akm09918c_start_measurement_blocking(const struct device * dev,enum sensor_channel chan)31 int akm09918c_start_measurement_blocking(const struct device *dev, enum sensor_channel chan)
32 {
33 	struct akm09918c_data *data = dev->data;
34 	const struct akm09918c_config *cfg = dev->config;
35 
36 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_MAGN_X && chan != SENSOR_CHAN_MAGN_Y &&
37 	    chan != SENSOR_CHAN_MAGN_Z && chan != SENSOR_CHAN_MAGN_XYZ) {
38 		LOG_DBG("Invalid channel %d", chan);
39 		return -EINVAL;
40 	}
41 
42 	if (data->mode == AKM09918C_CNTL2_PWR_DOWN) {
43 		if (i2c_reg_write_byte_dt(&cfg->i2c, AKM09918C_REG_CNTL2,
44 					  AKM09918C_CNTL2_SINGLE_MEASURE) != 0) {
45 			LOG_ERR("Failed to start measurement.");
46 			return -EIO;
47 		}
48 	}
49 	return 0;
50 }
51 
52 /**
53  * @brief Perform the bus transaction to fetch samples.
54  *
55  * @param dev Sensor device to operate on
56  * @param chan Channel ID to fetch
57  * @param x Location to write X channel sample.
58  * @param y Location to write Y channel sample.
59  * @param z Location to write Z channel sample.
60  * @return int 0 if successful or error code
61  */
akm09918c_fetch_measurement_blocking(const struct device * dev,int16_t * x,int16_t * y,int16_t * z)62 int akm09918c_fetch_measurement_blocking(const struct device *dev, int16_t *x, int16_t *y,
63 					 int16_t *z)
64 {
65 	const struct akm09918c_config *cfg = dev->config;
66 	uint8_t buf[9] = {0};
67 
68 	/* We have to read through the TMPS register or the data_ready bit won't clear */
69 	if (i2c_burst_read_dt(&cfg->i2c, AKM09918C_REG_ST1, buf, ARRAY_SIZE(buf)) != 0) {
70 		LOG_ERR("Failed to read sample data.");
71 		return -EIO;
72 	}
73 
74 	if (FIELD_GET(AKM09918C_ST1_DRDY, buf[0]) == 0) {
75 		LOG_ERR("Data not ready, st1=0x%02x", buf[0]);
76 		return -EBUSY;
77 	}
78 
79 	*x = sys_le16_to_cpu(buf[1] | (buf[2] << 8));
80 	*y = sys_le16_to_cpu(buf[3] | (buf[4] << 8));
81 	*z = sys_le16_to_cpu(buf[5] | (buf[6] << 8));
82 
83 	return 0;
84 }
85 
akm09918c_sample_fetch(const struct device * dev,enum sensor_channel chan)86 static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel chan)
87 {
88 	struct akm09918c_data *data = dev->data;
89 
90 	int ret = akm09918c_start_measurement_blocking(dev, chan);
91 
92 	if (ret) {
93 		return ret;
94 	}
95 	/* Wait for sample */
96 	LOG_DBG("Waiting for sample...");
97 	k_usleep(AKM09918C_MEASURE_TIME_US);
98 
99 	return akm09918c_fetch_measurement_blocking(dev, &data->x_sample, &data->y_sample,
100 						    &data->z_sample);
101 }
102 
akm09918c_convert(struct sensor_value * val,int16_t sample)103 static void akm09918c_convert(struct sensor_value *val, int16_t sample)
104 {
105 	int64_t conv_val = sample * AKM09918C_MICRO_GAUSS_PER_BIT;
106 
107 	val->val1 = conv_val / 1000000;
108 	val->val2 = conv_val - (val->val1 * 1000000);
109 }
110 
akm09918c_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)111 static int akm09918c_channel_get(const struct device *dev, enum sensor_channel chan,
112 				 struct sensor_value *val)
113 {
114 	struct akm09918c_data *data = dev->data;
115 
116 	if (chan == SENSOR_CHAN_MAGN_XYZ) {
117 		akm09918c_convert(val, data->x_sample);
118 		akm09918c_convert(val + 1, data->y_sample);
119 		akm09918c_convert(val + 2, data->z_sample);
120 	} else if (chan == SENSOR_CHAN_MAGN_X) {
121 		akm09918c_convert(val, data->x_sample);
122 	} else if (chan == SENSOR_CHAN_MAGN_Y) {
123 		akm09918c_convert(val, data->y_sample);
124 	} else if (chan == SENSOR_CHAN_MAGN_Z) {
125 		akm09918c_convert(val, data->z_sample);
126 	} else {
127 		LOG_DBG("Invalid channel %d", chan);
128 		return -ENOTSUP;
129 	}
130 
131 	return 0;
132 }
133 
akm09918c_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)134 static int akm09918c_attr_get(const struct device *dev, enum sensor_channel chan,
135 			      enum sensor_attribute attr, struct sensor_value *val)
136 {
137 	struct akm09918c_data *data = dev->data;
138 
139 	switch (chan) {
140 	case SENSOR_CHAN_MAGN_X:
141 	case SENSOR_CHAN_MAGN_Y:
142 	case SENSOR_CHAN_MAGN_Z:
143 	case SENSOR_CHAN_MAGN_XYZ:
144 		if (attr != SENSOR_ATTR_SAMPLING_FREQUENCY) {
145 			LOG_WRN("Invalid attribute %d", attr);
146 			return -EINVAL;
147 		}
148 		akm09918c_reg_to_hz(data->mode, val);
149 		break;
150 	default:
151 		LOG_WRN("Invalid channel %d", chan);
152 		return -EINVAL;
153 	}
154 	return 0;
155 }
156 
akm09918c_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)157 static int akm09918c_attr_set(const struct device *dev, enum sensor_channel chan,
158 			      enum sensor_attribute attr, const struct sensor_value *val)
159 {
160 	const struct akm09918c_config *cfg = dev->config;
161 	struct akm09918c_data *data = dev->data;
162 	int res;
163 
164 	switch (chan) {
165 	case SENSOR_CHAN_MAGN_X:
166 	case SENSOR_CHAN_MAGN_Y:
167 	case SENSOR_CHAN_MAGN_Z:
168 	case SENSOR_CHAN_MAGN_XYZ:
169 		if (attr != SENSOR_ATTR_SAMPLING_FREQUENCY) {
170 			LOG_WRN("Invalid attribute %d", attr);
171 			return -EINVAL;
172 		}
173 
174 		uint8_t mode = akm09918c_hz_to_reg(val);
175 
176 		res = i2c_reg_write_byte_dt(&cfg->i2c, AKM09918C_REG_CNTL2, mode);
177 		if (res != 0) {
178 			LOG_ERR("Failed to set sample frequency");
179 			return -EIO;
180 		}
181 		data->mode = mode;
182 		break;
183 	default:
184 		LOG_WRN("Invalid channel %d", chan);
185 		return -EINVAL;
186 	}
187 
188 	return 0;
189 }
190 
akm09918c_check_who_am_i(const struct i2c_dt_spec * i2c)191 static inline int akm09918c_check_who_am_i(const struct i2c_dt_spec *i2c)
192 {
193 	uint8_t buffer[2];
194 	int rc;
195 
196 	rc = i2c_burst_read_dt(i2c, AKM09918C_REG_WIA1, buffer, ARRAY_SIZE(buffer));
197 	if (rc != 0) {
198 		LOG_ERR("Failed to read who-am-i register (rc=%d)", rc);
199 		return -EIO;
200 	}
201 
202 	if (buffer[0] != AKM09918C_WIA1 || buffer[1] != AKM09918C_WIA2) {
203 		LOG_ERR("Wrong who-am-i value");
204 		return -EINVAL;
205 	}
206 
207 	return 0;
208 }
209 
akm09918c_init(const struct device * dev)210 static int akm09918c_init(const struct device *dev)
211 {
212 	const struct akm09918c_config *cfg = dev->config;
213 	struct akm09918c_data *data = dev->data;
214 	int rc;
215 
216 	if (!i2c_is_ready_dt(&cfg->i2c)) {
217 		LOG_ERR("I2C bus device not ready");
218 		return -ENODEV;
219 	}
220 
221 	/* Soft reset the chip */
222 	rc = i2c_reg_write_byte_dt(&cfg->i2c, AKM09918C_REG_CNTL3,
223 				   FIELD_PREP(AKM09918C_CNTL3_SRST, 1));
224 	if (rc != 0) {
225 		LOG_ERR("Failed to soft reset");
226 		return -EIO;
227 	}
228 
229 	/* check chip ID */
230 	rc = akm09918c_check_who_am_i(&cfg->i2c);
231 	if (rc != 0) {
232 		return rc;
233 	}
234 	data->mode = AKM09918C_CNTL2_PWR_DOWN;
235 #ifdef CONFIG_SENSOR_ASYNC_API
236 	/* init work for fetching after measurement has completed */
237 	k_work_init_delayable(&data->work_ctx.async_fetch_work, akm09918_async_fetch);
238 #endif
239 	return 0;
240 }
241 
242 static DEVICE_API(sensor, akm09918c_driver_api) = {
243 	.sample_fetch = akm09918c_sample_fetch,
244 	.channel_get = akm09918c_channel_get,
245 	.attr_get = akm09918c_attr_get,
246 	.attr_set = akm09918c_attr_set,
247 #ifdef CONFIG_SENSOR_ASYNC_API
248 	.submit = akm09918c_submit,
249 	.get_decoder = akm09918c_get_decoder,
250 #endif
251 };
252 
253 #define AKM09918C_DEFINE(inst)                                                                     \
254 	IF_ENABLED(CONFIG_I2C_RTIO, \
255 		(I2C_DT_IODEV_DEFINE(akm09918c_iodev_##inst, DT_DRV_INST(inst));)) \
256 	IF_ENABLED(CONFIG_I2C_RTIO, (RTIO_DEFINE( \
257 		akm09918c_rtio_ctx_##inst, CONFIG_I2C_RTIO_SQ_SIZE, CONFIG_I2C_RTIO_CQ_SIZE);)) \
258 	static struct akm09918c_data akm09918c_data_##inst = {                                     \
259 		IF_ENABLED(CONFIG_I2C_RTIO, (.rtio_ctx = \
260 			&akm09918c_rtio_ctx_##inst, .iodev = &akm09918c_iodev_##inst)) }; \
261 	static const struct akm09918c_config akm09918c_config_##inst = {                           \
262 		.i2c = I2C_DT_SPEC_INST_GET(inst),                                                 \
263 	};                                                                                         \
264                                                                                                    \
265 	SENSOR_DEVICE_DT_INST_DEFINE(inst, akm09918c_init, NULL, &akm09918c_data_##inst,           \
266 				     &akm09918c_config_##inst, POST_KERNEL,                        \
267 				     CONFIG_SENSOR_INIT_PRIORITY, &akm09918c_driver_api);
268 
269 DT_INST_FOREACH_STATUS_OKAY(AKM09918C_DEFINE)
270