1 /*
2  * Copyright (c) 2018 Philémon Jaermann
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT st_lsm303dlhc_magn
8 
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/init.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/logging/log.h>
13 
14 LOG_MODULE_REGISTER(lsm303dlhc_magn, CONFIG_SENSOR_LOG_LEVEL);
15 
16 #include "lsm303dlhc_magn.h"
17 
lsm303dlhc_sample_fetch(const struct device * dev,enum sensor_channel chan)18 static int lsm303dlhc_sample_fetch(const struct device *dev,
19 				   enum sensor_channel chan)
20 {
21 	const struct lsm303dlhc_magn_config *config = dev->config;
22 	struct lsm303dlhc_magn_data *drv_data = dev->data;
23 	uint8_t magn_buf[6];
24 	uint8_t status;
25 
26 	/* Check data ready flag */
27 	if (i2c_reg_read_byte_dt(&config->i2c, LSM303DLHC_SR_REG_M,
28 				 &status) < 0) {
29 		LOG_ERR("Failed to read status register.");
30 		return -EIO;
31 	}
32 
33 	if (!(status & LSM303DLHC_MAGN_DRDY)) {
34 		LOG_ERR("Sensor data not available.");
35 		return -EIO;
36 	}
37 
38 	if (i2c_burst_read_dt(&config->i2c, LSM303DLHC_REG_MAGN_X_LSB,
39 			      magn_buf, 6) < 0) {
40 		LOG_ERR("Could not read magn axis data.");
41 		return -EIO;
42 	}
43 
44 	drv_data->magn_x = (magn_buf[0] << 8) | magn_buf[1];
45 	drv_data->magn_y = (magn_buf[4] << 8) | magn_buf[5];
46 	drv_data->magn_z = (magn_buf[2] << 8) | magn_buf[3];
47 
48 	return 0;
49 }
50 
lsm303dlhc_convert_xy(struct sensor_value * val,int64_t raw_val)51 static void lsm303dlhc_convert_xy(struct sensor_value *val,
52 			       int64_t raw_val)
53 {
54 	val->val1 = raw_val / LSM303DLHC_MAGN_LSB_GAUSS_XY;
55 	val->val2 = (1000000 * raw_val / LSM303DLHC_MAGN_LSB_GAUSS_XY) % 1000000;
56 }
57 
lsm303dlhc_convert_z(struct sensor_value * val,int64_t raw_val)58 static void lsm303dlhc_convert_z(struct sensor_value *val,
59 			       int64_t raw_val)
60 {
61 	val->val1 = raw_val / LSM303DLHC_MAGN_LSB_GAUSS_Z;
62 	val->val2 = (1000000 * raw_val / LSM303DLHC_MAGN_LSB_GAUSS_Z) % 1000000;
63 }
64 
lsm303dlhc_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)65 static int lsm303dlhc_channel_get(const struct device *dev,
66 				  enum sensor_channel chan,
67 				  struct sensor_value *val)
68 {
69 	struct lsm303dlhc_magn_data *drv_data = dev->data;
70 
71 	switch (chan) {
72 	case  SENSOR_CHAN_MAGN_X:
73 		lsm303dlhc_convert_xy(val, drv_data->magn_x);
74 		break;
75 	case SENSOR_CHAN_MAGN_Y:
76 		lsm303dlhc_convert_xy(val, drv_data->magn_y);
77 		break;
78 	case SENSOR_CHAN_MAGN_Z:
79 		lsm303dlhc_convert_z(val, drv_data->magn_z);
80 		break;
81 	case SENSOR_CHAN_MAGN_XYZ:
82 		lsm303dlhc_convert_xy(val, drv_data->magn_x);
83 		lsm303dlhc_convert_xy(val + 1, drv_data->magn_y);
84 		lsm303dlhc_convert_z(val + 2, drv_data->magn_z);
85 		break;
86 	default:
87 		return -ENOTSUP;
88 	}
89 	return 0;
90 }
91 
92 static DEVICE_API(sensor, lsm303dlhc_magn_driver_api) = {
93 	.sample_fetch = lsm303dlhc_sample_fetch,
94 	.channel_get = lsm303dlhc_channel_get,
95 };
96 
lsm303dlhc_magn_init(const struct device * dev)97 static int lsm303dlhc_magn_init(const struct device *dev)
98 {
99 	const struct lsm303dlhc_magn_config *config = dev->config;
100 
101 	if (!device_is_ready(config->i2c.bus)) {
102 		LOG_ERR("I2C bus device not ready");
103 		return -ENODEV;
104 	}
105 
106 	/* Set magnetometer output data rate */
107 	if (i2c_reg_write_byte_dt(&config->i2c, LSM303DLHC_CRA_REG_M,
108 				  LSM303DLHC_MAGN_ODR_BITS) < 0) {
109 		LOG_ERR("Failed to configure chip.");
110 		return -EIO;
111 	}
112 
113 	/* Set magnetometer full scale range */
114 	if (i2c_reg_write_byte_dt(&config->i2c, LSM303DLHC_CRB_REG_M,
115 				  LSM303DLHC_MAGN_FS_BITS) < 0) {
116 		LOG_ERR("Failed to set magnetometer full scale range.");
117 		return -EIO;
118 	}
119 
120 	/* Continuous update */
121 	if (i2c_reg_write_byte_dt(&config->i2c, LSM303DLHC_MR_REG_M,
122 				  LSM303DLHC_MAGN_CONT_UPDATE) < 0) {
123 		LOG_ERR("Failed to enable continuous data update.");
124 		return -EIO;
125 	}
126 	return 0;
127 }
128 
129 #define LSM303DLHC_MAGN_DEFINE(inst)								\
130 	static struct lsm303dlhc_magn_data lsm303dlhc_magn_data_##inst;				\
131 												\
132 	static const struct lsm303dlhc_magn_config lsm303dlhc_magn_config_##inst = {		\
133 		.i2c = I2C_DT_SPEC_INST_GET(inst),						\
134 	};											\
135 												\
136 	SENSOR_DEVICE_DT_INST_DEFINE(inst, lsm303dlhc_magn_init, NULL,				\
137 			      &lsm303dlhc_magn_data_##inst, &lsm303dlhc_magn_config_##inst,	\
138 			      POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,				\
139 			      &lsm303dlhc_magn_driver_api);					\
140 
141 DT_INST_FOREACH_STATUS_OKAY(LSM303DLHC_MAGN_DEFINE)
142