1 /*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT honeywell_hmc5883l
8
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/init.h>
11 #include <zephyr/sys/__assert.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/drivers/sensor.h>
14 #include <string.h>
15 #include <zephyr/logging/log.h>
16
17 #include "hmc5883l.h"
18
19 LOG_MODULE_REGISTER(HMC5883L, CONFIG_SENSOR_LOG_LEVEL);
20
hmc5883l_convert(struct sensor_value * val,int16_t raw_val,uint16_t divider)21 static void hmc5883l_convert(struct sensor_value *val, int16_t raw_val,
22 uint16_t divider)
23 {
24 /* val = raw_val / divider */
25 val->val1 = raw_val / divider;
26 val->val2 = (((int64_t)raw_val % divider) * 1000000L) / divider;
27 }
28
hmc5883l_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)29 static int hmc5883l_channel_get(const struct device *dev,
30 enum sensor_channel chan,
31 struct sensor_value *val)
32 {
33 struct hmc5883l_data *drv_data = dev->data;
34
35 if (chan == SENSOR_CHAN_MAGN_X) {
36 hmc5883l_convert(val, drv_data->x_sample,
37 hmc5883l_gain[drv_data->gain_idx]);
38 } else if (chan == SENSOR_CHAN_MAGN_Y) {
39 hmc5883l_convert(val, drv_data->y_sample,
40 hmc5883l_gain[drv_data->gain_idx]);
41 } else if (chan == SENSOR_CHAN_MAGN_Z) {
42 hmc5883l_convert(val, drv_data->z_sample,
43 hmc5883l_gain[drv_data->gain_idx]);
44 } else if (chan == SENSOR_CHAN_MAGN_XYZ) {
45 hmc5883l_convert(val, drv_data->x_sample,
46 hmc5883l_gain[drv_data->gain_idx]);
47 hmc5883l_convert(val + 1, drv_data->y_sample,
48 hmc5883l_gain[drv_data->gain_idx]);
49 hmc5883l_convert(val + 2, drv_data->z_sample,
50 hmc5883l_gain[drv_data->gain_idx]);
51 } else {
52 return -ENOTSUP;
53 }
54
55 return 0;
56 }
57
hmc5883l_sample_fetch(const struct device * dev,enum sensor_channel chan)58 static int hmc5883l_sample_fetch(const struct device *dev,
59 enum sensor_channel chan)
60 {
61 struct hmc5883l_data *drv_data = dev->data;
62 const struct hmc5883l_config *config = dev->config;
63 int16_t buf[3];
64
65 __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
66
67 /* fetch magnetometer sample */
68 if (i2c_burst_read_dt(&config->i2c, HMC5883L_REG_DATA_START,
69 (uint8_t *)buf, 6) < 0) {
70 LOG_ERR("Failed to fetch magnetometer sample.");
71 return -EIO;
72 }
73
74 drv_data->x_sample = sys_be16_to_cpu(buf[0]);
75 drv_data->z_sample = sys_be16_to_cpu(buf[1]);
76 drv_data->y_sample = sys_be16_to_cpu(buf[2]);
77
78 return 0;
79 }
80
81 static DEVICE_API(sensor, hmc5883l_driver_api) = {
82 #if CONFIG_HMC5883L_TRIGGER
83 .trigger_set = hmc5883l_trigger_set,
84 #endif
85 .sample_fetch = hmc5883l_sample_fetch,
86 .channel_get = hmc5883l_channel_get,
87 };
88
hmc5883l_init(const struct device * dev)89 int hmc5883l_init(const struct device *dev)
90 {
91 struct hmc5883l_data *drv_data = dev->data;
92 const struct hmc5883l_config *config = dev->config;
93 uint8_t chip_cfg[3], id[3], idx;
94
95 if (!device_is_ready(config->i2c.bus)) {
96 LOG_ERR("I2C bus device not ready");
97 return -ENODEV;
98 }
99
100 /* check chip ID */
101 if (i2c_burst_read_dt(&config->i2c, HMC5883L_REG_CHIP_ID, id, 3) < 0) {
102 LOG_ERR("Failed to read chip ID.");
103 return -EIO;
104 }
105
106 if (id[0] != HMC5883L_CHIP_ID_A || id[1] != HMC5883L_CHIP_ID_B ||
107 id[2] != HMC5883L_CHIP_ID_C) {
108 LOG_ERR("Invalid chip ID.");
109 return -EINVAL;
110 }
111
112 /* check if CONFIG_HMC5883L_FS is valid */
113 for (idx = 0U; idx < ARRAY_SIZE(hmc5883l_fs_strings); idx++) {
114 if (!strcmp(hmc5883l_fs_strings[idx], CONFIG_HMC5883L_FS)) {
115 break;
116 }
117 }
118
119 if (idx == ARRAY_SIZE(hmc5883l_fs_strings)) {
120 LOG_ERR("Invalid full-scale range value.");
121 return -EINVAL;
122 }
123
124 drv_data->gain_idx = idx;
125
126 /* check if CONFIG_HMC5883L_ODR is valid */
127 for (idx = 0U; idx < ARRAY_SIZE(hmc5883l_odr_strings); idx++) {
128 if (!strcmp(hmc5883l_odr_strings[idx], CONFIG_HMC5883L_ODR)) {
129 break;
130 }
131 }
132
133 if (idx == ARRAY_SIZE(hmc5883l_odr_strings)) {
134 LOG_ERR("Invalid ODR value.");
135 return -EINVAL;
136 }
137
138 /* configure device */
139 chip_cfg[0] = idx << HMC5883L_ODR_SHIFT;
140 chip_cfg[1] = drv_data->gain_idx << HMC5883L_GAIN_SHIFT;
141 chip_cfg[2] = HMC5883L_MODE_CONTINUOUS;
142
143 if (i2c_burst_write_dt(&config->i2c, HMC5883L_REG_CONFIG_A,
144 chip_cfg, 3) < 0) {
145 LOG_ERR("Failed to configure chip.");
146 return -EIO;
147 }
148
149 #ifdef CONFIG_HMC5883L_TRIGGER
150 if (config->int_gpio.port) {
151 if (hmc5883l_init_interrupt(dev) < 0) {
152 LOG_ERR("Failed to initialize interrupts.");
153 return -EIO;
154 }
155 }
156 #endif
157
158 return 0;
159 }
160
161 #define HMC5883L_DEFINE(inst) \
162 static struct hmc5883l_data hmc5883l_data_##inst; \
163 \
164 static const struct hmc5883l_config hmc5883l_config_##inst = { \
165 .i2c = I2C_DT_SPEC_INST_GET(inst), \
166 IF_ENABLED(CONFIG_HMC5883L_TRIGGER, \
167 (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, { 0 }),)) \
168 }; \
169 \
170 SENSOR_DEVICE_DT_INST_DEFINE(inst, hmc5883l_init, NULL, \
171 &hmc5883l_data_##inst, &hmc5883l_config_##inst, POST_KERNEL, \
172 CONFIG_SENSOR_INIT_PRIORITY, &hmc5883l_driver_api); \
173
174 DT_INST_FOREACH_STATUS_OKAY(HMC5883L_DEFINE)
175