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