Skip to content

Commit c1f3e2c

Browse files
ubiedakartben
authored andcommitted
sensor: rm3100: Basic functionality
This patch introduces rm3100 magnetometer sensor, with basic support (only read-decode). This driver has bus support for I2C. Signed-off-by: Luis Ubieda <luisf@croxel.com>
1 parent 8f8b223 commit c1f3e2c

File tree

13 files changed

+640
-0
lines changed

13 files changed

+640
-0
lines changed

drivers/sensor/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ add_subdirectory(nordic)
2222
add_subdirectory(nuvoton)
2323
add_subdirectory(nxp)
2424
add_subdirectory(pixart)
25+
add_subdirectory(pni)
2526
add_subdirectory(realtek)
2627
add_subdirectory(renesas)
2728
add_subdirectory(rohm)

drivers/sensor/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ source "drivers/sensor/nordic/Kconfig"
108108
source "drivers/sensor/nuvoton/Kconfig"
109109
source "drivers/sensor/nxp/Kconfig"
110110
source "drivers/sensor/pixart/Kconfig"
111+
source "drivers/sensor/pni/Kconfig"
111112
source "drivers/sensor/realtek/Kconfig"
112113
source "drivers/sensor/renesas/Kconfig"
113114
source "drivers/sensor/rohm/Kconfig"

drivers/sensor/pni/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) 2025 Croxel, Inc.
2+
# Copyright (c) 2025 CogniPilot Foundation
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# zephyr-keep-sorted-start
6+
add_subdirectory_ifdef(CONFIG_RM3100 rm3100)
7+
# zephyr-keep-sorted-stop

drivers/sensor/pni/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) 2025 Croxel, Inc.
2+
# Copyright (c) 2025 CogniPilot Foundation
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# zephyr-keep-sorted-start
6+
source "drivers/sensor/pni/rm3100/Kconfig"
7+
# zephyr-keep-sorted-stop
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 Croxel, Inc.
2+
# Copyright (c) 2025 CogniPilot Foundation
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
zephyr_library()
6+
zephyr_library_sources(
7+
rm3100.c
8+
rm3100_decoder.c
9+
)

drivers/sensor/pni/rm3100/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) 2025 Croxel, Inc.
2+
# Copyright (c) 2025 CogniPilot Foundation
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config RM3100
6+
bool "RM3100 3-Axis Magnetometer"
7+
default y
8+
depends on DT_HAS_PNI_RM3100_ENABLED
9+
select I2C
10+
select I2C_RTIO
11+
select SENSOR_ASYNC_API
12+
help
13+
Enable driver for PNI RM3100 high-accuracy 3-axis magnetometer.

drivers/sensor/pni/rm3100/rm3100.c

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
* Copyright (c) 2025 Croxel, Inc.
3+
* Copyright (c) 2025 CogniPilot Foundation
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#define DT_DRV_COMPAT pni_rm3100
9+
10+
#include <zephyr/drivers/sensor.h>
11+
#include <zephyr/drivers/i2c.h>
12+
#include <zephyr/rtio/rtio.h>
13+
#include <zephyr/rtio/work.h>
14+
#include <zephyr/sys/check.h>
15+
16+
#include "rm3100.h"
17+
#include "rm3100_reg.h"
18+
#include "rm3100_bus.h"
19+
#include "rm3100_decoder.h"
20+
21+
#include <zephyr/logging/log.h>
22+
LOG_MODULE_REGISTER(RM3100, CONFIG_SENSOR_LOG_LEVEL);
23+
24+
static void rm3100_complete_result(struct rtio *ctx, const struct rtio_sqe *sqe, void *arg)
25+
{
26+
struct rtio_iodev_sqe *iodev_sqe = (struct rtio_iodev_sqe *)sqe->userdata;
27+
struct rtio_cqe *cqe;
28+
int err = 0;
29+
30+
do {
31+
cqe = rtio_cqe_consume(ctx);
32+
if (cqe != NULL) {
33+
err = cqe->result;
34+
rtio_cqe_release(ctx, cqe);
35+
}
36+
} while (cqe != NULL);
37+
38+
if (err) {
39+
rtio_iodev_sqe_err(iodev_sqe, err);
40+
} else {
41+
rtio_iodev_sqe_ok(iodev_sqe, 0);
42+
}
43+
44+
LOG_DBG("One-shot fetch completed");
45+
}
46+
47+
static void rm3100_submit_one_shot(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
48+
{
49+
const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
50+
const struct sensor_chan_spec *const channels = cfg->channels;
51+
const size_t num_channels = cfg->count;
52+
uint32_t min_buf_len = sizeof(struct rm3100_encoded_data);
53+
int err;
54+
uint8_t *buf;
55+
uint32_t buf_len;
56+
struct rm3100_encoded_data *edata;
57+
struct rm3100_data *data = dev->data;
58+
59+
err = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
60+
if (err) {
61+
LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
62+
rtio_iodev_sqe_err(iodev_sqe, err);
63+
return;
64+
}
65+
66+
edata = (struct rm3100_encoded_data *)buf;
67+
68+
err = rm3100_encode(dev, channels, num_channels, buf);
69+
if (err != 0) {
70+
LOG_ERR("Failed to encode sensor data");
71+
rtio_iodev_sqe_err(iodev_sqe, err);
72+
return;
73+
}
74+
75+
struct rtio_sqe *write_sqe = rtio_sqe_acquire(data->rtio.ctx);
76+
struct rtio_sqe *read_sqe = rtio_sqe_acquire(data->rtio.ctx);
77+
struct rtio_sqe *complete_sqe = rtio_sqe_acquire(data->rtio.ctx);
78+
79+
if (!write_sqe || !read_sqe || !complete_sqe) {
80+
LOG_ERR("Failed to acquire RTIO SQEs");
81+
rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
82+
return;
83+
}
84+
85+
uint8_t val = RM3100_REG_MX;
86+
87+
rtio_sqe_prep_tiny_write(write_sqe,
88+
data->rtio.iodev,
89+
RTIO_PRIO_HIGH,
90+
&val,
91+
1,
92+
NULL);
93+
write_sqe->flags |= RTIO_SQE_TRANSACTION;
94+
95+
rtio_sqe_prep_read(read_sqe,
96+
data->rtio.iodev,
97+
RTIO_PRIO_HIGH,
98+
edata->payload,
99+
sizeof(edata->payload),
100+
NULL);
101+
read_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART;
102+
read_sqe->flags |= RTIO_SQE_CHAINED;
103+
104+
rtio_sqe_prep_callback_no_cqe(complete_sqe,
105+
rm3100_complete_result,
106+
(void *)dev,
107+
iodev_sqe);
108+
109+
rtio_submit(data->rtio.ctx, 0);
110+
}
111+
112+
static void rm3100_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
113+
{
114+
const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
115+
116+
if (!cfg->is_streaming) {
117+
rm3100_submit_one_shot(dev, iodev_sqe);
118+
} else {
119+
LOG_ERR("Streaming not supported");
120+
rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
121+
}
122+
}
123+
124+
/* This will be implemented later */
125+
static DEVICE_API(sensor, rm3100_driver_api) = {
126+
/* API functions will be added here */
127+
.submit = rm3100_submit,
128+
.get_decoder = rm3100_get_decoder,
129+
};
130+
131+
static int rm3100_init(const struct device *dev)
132+
{
133+
uint8_t val;
134+
int err;
135+
136+
/* Check device ID to make sure we can talk to the sensor */
137+
err = rm3100_bus_read(dev, RM3100_REG_REVID, &val, 1);
138+
if (err < 0) {
139+
LOG_ERR("Failed to read chip ID");
140+
return err;
141+
} else if (val != RM3100_REVID_VALUE) {
142+
LOG_ERR("Invalid chip ID: 0x%02x, expected 0x%02x",
143+
val, RM3100_REVID_VALUE);
144+
return -ENODEV;
145+
}
146+
LOG_DBG("RM3100 chip ID confirmed: 0x%02x", val);
147+
148+
/** Enable Continuous measurement on all axis */
149+
val = RM3100_CMM_ALL_AXIS;
150+
151+
err = rm3100_bus_write(dev, RM3100_REG_CMM, &val, 1);
152+
if (err < 0) {
153+
LOG_ERR("Failed to set sensor in Continuous Measurement Mode: %d", err);
154+
return err;
155+
}
156+
157+
return 0;
158+
}
159+
160+
#define RM3100_DEFINE(inst) \
161+
\
162+
RTIO_DEFINE(rm3100_rtio_ctx_##inst, 8, 8); \
163+
I2C_DT_IODEV_DEFINE(rm3100_bus_##inst, DT_DRV_INST(inst)); \
164+
\
165+
static const struct rm3100_config rm3100_cfg_##inst; \
166+
\
167+
static struct rm3100_data rm3100_data_##inst = { \
168+
.rtio = { \
169+
.iodev = &rm3100_bus_##inst, \
170+
.ctx = &rm3100_rtio_ctx_##inst, \
171+
}, \
172+
}; \
173+
\
174+
SENSOR_DEVICE_DT_INST_DEFINE(inst, rm3100_init, NULL, \
175+
&rm3100_data_##inst, \
176+
&rm3100_cfg_##inst, \
177+
POST_KERNEL, \
178+
CONFIG_SENSOR_INIT_PRIORITY, \
179+
&rm3100_driver_api);
180+
181+
DT_INST_FOREACH_STATUS_OKAY(RM3100_DEFINE)

drivers/sensor/pni/rm3100/rm3100.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (c) 2025 Croxel, Inc.
3+
* Copyright (c) 2025 CogniPilot Foundation
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#ifndef ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_
9+
#define ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_
10+
11+
#include <zephyr/drivers/sensor.h>
12+
#include <zephyr/rtio/rtio.h>
13+
#include "rm3100_reg.h"
14+
15+
/* RM3100 produces 3 bytes (24-bit) of data per axis */
16+
#define RM3100_BYTES_PER_AXIS 3
17+
#define RM3100_TOTAL_BYTES (RM3100_BYTES_PER_AXIS * 3)
18+
19+
struct rm3100_encoded_data {
20+
struct {
21+
uint64_t timestamp;
22+
uint8_t channels : 3;
23+
} header;
24+
union {
25+
uint8_t payload[RM3100_TOTAL_BYTES];
26+
struct {
27+
uint32_t x : 24;
28+
uint32_t y : 24;
29+
uint32_t z : 24;
30+
} __attribute__((__packed__)) magn;
31+
};
32+
};
33+
34+
struct rm3100_config {
35+
uint32_t unused; /* Will be expanded with stremaing-mode to hold int-gpios */
36+
};
37+
38+
struct rm3100_data {
39+
/* RTIO context */
40+
struct {
41+
struct rtio_iodev *iodev;
42+
struct rtio *ctx;
43+
} rtio;
44+
};
45+
46+
#endif /* ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_ */
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (c) 2025 Croxel Inc.
3+
* Copyright (c) 2025 CogniPilot Foundation
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#ifndef ZEPHYR_DRIVERS_SENSOR_RM3100_BUS_H_
9+
#define ZEPHYR_DRIVERS_SENSOR_RM3100_BUS_H_
10+
11+
#include <stdint.h>
12+
#include <zephyr/rtio/rtio.h>
13+
14+
#include "rm3100.h"
15+
#include "rm3100_reg.h"
16+
17+
static inline int rm3100_bus_read(const struct device *dev,
18+
uint8_t reg,
19+
uint8_t *buf,
20+
uint16_t len)
21+
{
22+
struct rm3100_data *data = dev->data;
23+
struct rtio *ctx = data->rtio.ctx;
24+
struct rtio_iodev *iodev = data->rtio.iodev;
25+
struct rtio_sqe *write_sqe = rtio_sqe_acquire(ctx);
26+
struct rtio_sqe *read_sqe = rtio_sqe_acquire(ctx);
27+
struct rtio_cqe *cqe;
28+
int err;
29+
30+
if (!write_sqe || !read_sqe) {
31+
return -ENOMEM;
32+
}
33+
34+
rtio_sqe_prep_write(write_sqe, iodev, RTIO_PRIO_HIGH, &reg, 1, NULL);
35+
write_sqe->flags |= RTIO_SQE_TRANSACTION;
36+
rtio_sqe_prep_read(read_sqe, iodev, RTIO_PRIO_HIGH, buf, len, NULL);
37+
read_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART;
38+
39+
err = rtio_submit(ctx, 2);
40+
if (err) {
41+
return err;
42+
}
43+
44+
do {
45+
cqe = rtio_cqe_consume(ctx);
46+
if (cqe != NULL) {
47+
err = cqe->result;
48+
rtio_cqe_release(ctx, cqe);
49+
}
50+
} while (cqe != NULL);
51+
52+
return err;
53+
}
54+
55+
static inline int rm3100_bus_write(const struct device *dev,
56+
uint8_t reg,
57+
const uint8_t *buf,
58+
uint16_t len)
59+
{
60+
struct rm3100_data *data = dev->data;
61+
struct rtio *ctx = data->rtio.ctx;
62+
struct rtio_iodev *iodev = data->rtio.iodev;
63+
struct rtio_sqe *write_reg_sqe = rtio_sqe_acquire(ctx);
64+
struct rtio_sqe *write_buf_sqe = rtio_sqe_acquire(ctx);
65+
struct rtio_cqe *cqe;
66+
int err;
67+
68+
if (!write_reg_sqe || !write_buf_sqe) {
69+
return -ENOMEM;
70+
}
71+
72+
rtio_sqe_prep_write(write_reg_sqe, iodev, RTIO_PRIO_HIGH, &reg, 1, NULL);
73+
write_reg_sqe->flags |= RTIO_SQE_TRANSACTION;
74+
rtio_sqe_prep_write(write_buf_sqe, iodev, RTIO_PRIO_HIGH, buf, len, NULL);
75+
write_buf_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP;
76+
77+
err = rtio_submit(ctx, 2);
78+
if (err) {
79+
return err;
80+
}
81+
82+
do {
83+
cqe = rtio_cqe_consume(ctx);
84+
if (cqe != NULL) {
85+
err = cqe->result;
86+
rtio_cqe_release(ctx, cqe);
87+
}
88+
} while (cqe != NULL);
89+
90+
return err;
91+
}
92+
93+
#endif /* ZEPHYR_DRIVERS_SENSOR_RM3100_BUS_H_ */

0 commit comments

Comments
 (0)