Skip to content

Commit e8bd35e

Browse files
gautierg-stteburd
authored andcommitted
drivers: i2c: stm32 i2cv2 controller driver using rtio
Adds the simplest possible I2Cv2 controller driver for STM32 with the RTIO interface. Currently only interrupt driven transfers are supported. Signed-off-by: Guillaume Gautier <guillaume.gautier-ext@st.com> Co-authored-by: Tom Burdick <thomas.burdick@intel.com>
1 parent 2397a63 commit e8bd35e

File tree

4 files changed

+562
-11
lines changed

4 files changed

+562
-11
lines changed

drivers/i2c/CMakeLists.txt

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,6 @@ zephyr_library_sources_ifdef(CONFIG_I2C_SC18IM704 i2c_sc18im704.c)
6666
zephyr_library_sources_ifdef(CONFIG_I2C_SEDI i2c_sedi.c)
6767
zephyr_library_sources_ifdef(CONFIG_I2C_SIFIVE i2c_sifive.c)
6868
zephyr_library_sources_ifdef(CONFIG_I2C_SMARTBOND i2c_smartbond.c)
69-
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1
70-
i2c_ll_stm32_common.c
71-
i2c_ll_stm32_v1.c
72-
i2c_ll_stm32.c
73-
)
74-
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2
75-
i2c_ll_stm32_common.c
76-
i2c_ll_stm32_v2.c
77-
i2c_ll_stm32.c
78-
)
7969
zephyr_library_sources_ifdef(CONFIG_I2C_SY1XX i2c_sy1xx.c)
8070
zephyr_library_sources_ifdef(CONFIG_I2C_TCA954X i2c_tca954x.c)
8171
zephyr_library_sources_ifdef(CONFIG_I2C_TELINK_B91 i2c_b91.c)
@@ -94,6 +84,9 @@ if(CONFIG_I2C_RTIO)
9484
zephyr_library_sources_ifdef(CONFIG_I2C_NRFX_TWIM i2c_nrfx_twim_common.c)
9585
zephyr_library_sources_ifdef(CONFIG_I2C_NRFX_TWIM i2c_nrfx_twim_rtio.c)
9686
zephyr_library_sources_ifdef(CONFIG_I2C_SAM_TWIHS i2c_sam_twihs_rtio.c)
87+
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32_common.c)
88+
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32_rtio.c)
89+
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32_v2_rtio.c)
9790
# zephyr-keep-sorted-stop
9891
else()
9992
# zephyr-keep-sorted-start
@@ -104,5 +97,11 @@ else()
10497
zephyr_library_sources_ifdef(CONFIG_I2C_NRFX_TWIM i2c_nrfx_twim.c)
10598
zephyr_library_sources_ifdef(CONFIG_I2C_NRFX_TWIM i2c_nrfx_twim_common.c)
10699
zephyr_library_sources_ifdef(CONFIG_I2C_SAM_TWIHS i2c_sam_twihs.c)
100+
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1 i2c_ll_stm32.c)
101+
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1 i2c_ll_stm32_common.c)
102+
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1 i2c_ll_stm32_v1.c)
103+
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32.c)
104+
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32_common.c)
105+
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32_v2.c)
107106
# zephyr-keep-sorted-stop
108107
endif()

drivers/i2c/i2c_ll_stm32.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ struct i2c_stm32_config {
6969
};
7070

7171
struct i2c_stm32_data {
72+
#ifdef CONFIG_I2C_RTIO
73+
struct i2c_rtio *ctx;
74+
uint32_t dev_config;
75+
uint8_t *xfer_buf;
76+
uint8_t xfer_len;
77+
uint8_t xfer_flags;
78+
#else /* CONFIG_I2C_RTIO */
7279
#ifdef CONFIG_I2C_STM32_INTERRUPT
7380
struct k_sem device_sync_sem;
7481
#endif /* CONFIG_I2C_STM32_INTERRUPT */
@@ -114,20 +121,27 @@ struct i2c_stm32_data {
114121
struct dma_config dma_rx_cfg;
115122
struct dma_block_config dma_blk_cfg;
116123
#endif /* CONFIG_I2C_STM32_V2_DMA */
124+
#endif /* CONFIG_I2C_RTIO */
117125
};
118126

127+
#ifdef CONFIG_I2C_RTIO
128+
bool i2c_stm32_start(const struct device *dev);
129+
int i2c_stm32_msg_start(const struct device *dev, uint8_t flags,
130+
uint8_t *buf, size_t buf_len, uint16_t i2c_addr);
131+
#else /* CONFIG_I2C_RTIO */
119132
int i2c_stm32_transaction(const struct device *dev,
120133
struct i2c_msg msg, uint8_t *next_msg_flags,
121134
uint16_t periph);
122-
int i2c_stm32_configure_timing(const struct device *dev, uint32_t clk);
123135
int i2c_stm32_runtime_configure(const struct device *dev, uint32_t config);
124136

125137
#ifdef CONFIG_I2C_TARGET
126138
int i2c_stm32_target_register(const struct device *dev, struct i2c_target_config *config);
127139
int i2c_stm32_target_unregister(const struct device *dev, struct i2c_target_config *config);
128140
#endif /* CONFIG_I2C_TARGET */
141+
#endif /* CONFIG_I2C_RTIO */
129142

130143
int i2c_stm32_activate(const struct device *dev);
144+
int i2c_stm32_configure_timing(const struct device *dev, uint32_t clk);
131145

132146
#ifdef CONFIG_PM_DEVICE
133147
int i2c_stm32_pm_action(const struct device *dev, enum pm_device_action action);

drivers/i2c/i2c_ll_stm32_rtio.c

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
/*
2+
* Copyright (c) 2025 STMicroelectronics
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <errno.h>
8+
#include <soc.h>
9+
#include <stm32_ll_i2c.h>
10+
#include <stm32_ll_rcc.h>
11+
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
12+
#include <zephyr/drivers/clock_control.h>
13+
#include <zephyr/drivers/i2c.h>
14+
#include <zephyr/drivers/i2c/rtio.h>
15+
#include <zephyr/drivers/pinctrl.h>
16+
#include <zephyr/kernel.h>
17+
#include <zephyr/pm/device.h>
18+
#include <zephyr/pm/device_runtime.h>
19+
#include <zephyr/sys/util.h>
20+
21+
#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
22+
#include <zephyr/logging/log.h>
23+
LOG_MODULE_REGISTER(i2c_ll_stm32_rtio);
24+
25+
#include "i2c_ll_stm32.h"
26+
#include "i2c-priv.h"
27+
28+
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_i2c_v2)
29+
#define DT_DRV_COMPAT st_stm32_i2c_v2
30+
#else
31+
#define DT_DRV_COMPAT st_stm32_i2c_v1
32+
#endif
33+
34+
/* This symbol takes the value 1 if one of the device instances */
35+
/* is configured in dts with a domain clock */
36+
#if STM32_DT_INST_DEV_DOMAIN_CLOCK_SUPPORT
37+
#define I2C_STM32_DOMAIN_CLOCK_SUPPORT 1
38+
#else
39+
#define I2C_STM32_DOMAIN_CLOCK_SUPPORT 0
40+
#endif
41+
42+
static int i2c_stm32_do_configure(const struct device *dev, uint32_t config)
43+
{
44+
const struct i2c_stm32_config *cfg = dev->config;
45+
struct i2c_stm32_data *data = dev->data;
46+
const struct device *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
47+
I2C_TypeDef *i2c = cfg->i2c;
48+
uint32_t i2c_clock = 0U;
49+
int ret;
50+
51+
if (IS_ENABLED(I2C_STM32_DOMAIN_CLOCK_SUPPORT) && (cfg->pclk_len > 1)) {
52+
if (clock_control_get_rate(clk, (clock_control_subsys_t)&cfg->pclken[1],
53+
&i2c_clock) < 0) {
54+
LOG_ERR("Failed call clock_control_get_rate(pclken[1])");
55+
return -EIO;
56+
}
57+
} else {
58+
if (clock_control_get_rate(clk, (clock_control_subsys_t)&cfg->pclken[0],
59+
&i2c_clock) < 0) {
60+
LOG_ERR("Failed call clock_control_get_rate(pclken[0])");
61+
return -EIO;
62+
}
63+
}
64+
65+
data->dev_config = config;
66+
67+
#ifdef CONFIG_PM_DEVICE_RUNTIME
68+
ret = clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken[0]);
69+
if (ret < 0) {
70+
LOG_ERR("Failed enabling I2C clock");
71+
return ret;
72+
}
73+
#endif
74+
75+
LL_I2C_Disable(i2c);
76+
ret = i2c_stm32_configure_timing(dev, i2c_clock);
77+
if (ret < 0) {
78+
LOG_ERR("Failed configuring I2C timing");
79+
return ret;
80+
}
81+
82+
#ifdef CONFIG_PM_DEVICE_RUNTIME
83+
ret = clock_control_off(clk, (clock_control_subsys_t)&cfg->pclken[0]);
84+
if (ret < 0) {
85+
LOG_ERR("Failed disabling I2C clock");
86+
return ret;
87+
}
88+
#endif
89+
90+
return ret;
91+
}
92+
93+
bool i2c_stm32_start(const struct device *dev)
94+
{
95+
struct i2c_stm32_data *data = dev->data;
96+
struct i2c_rtio *ctx = data->ctx;
97+
struct rtio_sqe *sqe = &ctx->txn_curr->sqe;
98+
struct i2c_dt_spec *dt_spec = sqe->iodev->data;
99+
100+
int res = 0;
101+
102+
switch (sqe->op) {
103+
case RTIO_OP_RX:
104+
return i2c_stm32_msg_start(dev, I2C_MSG_READ | sqe->iodev_flags,
105+
sqe->rx.buf, sqe->rx.buf_len, dt_spec->addr);
106+
case RTIO_OP_TINY_TX:
107+
return i2c_stm32_msg_start(dev, sqe->iodev_flags,
108+
sqe->tiny_tx.buf, sqe->tiny_tx.buf_len, dt_spec->addr);
109+
case RTIO_OP_TX:
110+
return i2c_stm32_msg_start(dev, sqe->iodev_flags,
111+
(uint8_t *)sqe->tx.buf, sqe->tx.buf_len, dt_spec->addr);
112+
case RTIO_OP_I2C_CONFIGURE:
113+
res = i2c_stm32_do_configure(dev, sqe->i2c_config);
114+
return i2c_rtio_complete(data->ctx, res);
115+
default:
116+
LOG_ERR("Invalid op code %d for submission %p\n", sqe->op, (void *)sqe);
117+
return i2c_rtio_complete(data->ctx, -EINVAL);
118+
}
119+
}
120+
121+
static int i2c_stm32_configure(const struct device *dev,
122+
uint32_t dev_config_raw)
123+
{
124+
struct i2c_stm32_data *data = dev->data;
125+
struct i2c_rtio *const ctx = data->ctx;
126+
127+
return i2c_rtio_configure(ctx, dev_config_raw);
128+
}
129+
130+
static int i2c_stm32_transfer(const struct device *dev, struct i2c_msg *msgs,
131+
uint8_t num_msgs, uint16_t addr)
132+
{
133+
struct i2c_stm32_data *data = dev->data;
134+
struct i2c_rtio *const ctx = data->ctx;
135+
136+
return i2c_rtio_transfer(ctx, msgs, num_msgs, addr);
137+
}
138+
139+
int i2c_stm32_get_config(const struct device *dev, uint32_t *config)
140+
{
141+
struct i2c_stm32_data *data = dev->data;
142+
143+
*config = data->dev_config;
144+
145+
return 0;
146+
}
147+
148+
static void i2c_stm32_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
149+
{
150+
struct i2c_stm32_data *data = dev->data;
151+
struct i2c_rtio *const ctx = data->ctx;
152+
153+
if (i2c_rtio_submit(ctx, iodev_sqe)) {
154+
i2c_stm32_start(dev);
155+
}
156+
}
157+
158+
static const struct i2c_driver_api api_funcs = {
159+
.configure = i2c_stm32_configure,
160+
.transfer = i2c_stm32_transfer,
161+
.get_config = i2c_stm32_get_config,
162+
.iodev_submit = i2c_stm32_submit,
163+
};
164+
165+
static int i2c_stm32_init(const struct device *dev)
166+
{
167+
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
168+
const struct i2c_stm32_config *cfg = dev->config;
169+
uint32_t bitrate_cfg;
170+
int ret;
171+
struct i2c_stm32_data *data = dev->data;
172+
173+
cfg->irq_config_func(dev);
174+
175+
i2c_rtio_init(data->ctx, dev);
176+
177+
if (!device_is_ready(clk)) {
178+
LOG_ERR("clock control device not ready");
179+
return -ENODEV;
180+
}
181+
182+
i2c_stm32_activate(dev);
183+
184+
if (IS_ENABLED(I2C_STM32_DOMAIN_CLOCK_SUPPORT) && (cfg->pclk_len > 1)) {
185+
/* Enable I2C clock source */
186+
ret = clock_control_configure(clk,
187+
(clock_control_subsys_t) &cfg->pclken[1],
188+
NULL);
189+
if (ret < 0) {
190+
return -EIO;
191+
}
192+
}
193+
194+
#if defined(CONFIG_SOC_SERIES_STM32F1X)
195+
/*
196+
* Force i2c reset for STM32F1 series.
197+
* So that they can enter master mode properly.
198+
* Issue described in ES096 2.14.7
199+
*/
200+
I2C_TypeDef *i2c = cfg->i2c;
201+
202+
LL_I2C_EnableReset(i2c);
203+
LL_I2C_DisableReset(i2c);
204+
#endif
205+
206+
bitrate_cfg = i2c_map_dt_bitrate(cfg->bitrate);
207+
208+
ret = i2c_stm32_do_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg);
209+
if (ret < 0) {
210+
LOG_ERR("i2c: failure initializing");
211+
return ret;
212+
}
213+
214+
#ifdef CONFIG_PM_DEVICE_RUNTIME
215+
(void)pm_device_runtime_enable(dev);
216+
#endif
217+
218+
return 0;
219+
}
220+
221+
#define I2C_STM32_INIT(index) \
222+
I2C_STM32_IRQ_HANDLER_DECL(index); \
223+
\
224+
IF_ENABLED(DT_HAS_COMPAT_STATUS_OKAY(st_stm32_i2c_v2), \
225+
(static const uint32_t i2c_timings_##index[] = \
226+
DT_INST_PROP_OR(index, timings, {});)) \
227+
\
228+
PINCTRL_DT_INST_DEFINE(index); \
229+
\
230+
static const struct stm32_pclken pclken_##index[] = \
231+
STM32_DT_INST_CLOCKS(index); \
232+
\
233+
static const struct i2c_stm32_config i2c_stm32_cfg_##index = { \
234+
.i2c = (I2C_TypeDef *)DT_INST_REG_ADDR(index), \
235+
.pclken = pclken_##index, \
236+
.pclk_len = DT_INST_NUM_CLOCKS(index), \
237+
I2C_STM32_IRQ_HANDLER_FUNCTION(index) \
238+
.bitrate = DT_INST_PROP(index, clock_frequency), \
239+
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
240+
IF_ENABLED(DT_HAS_COMPAT_STATUS_OKAY(st_stm32_i2c_v2), \
241+
(.timings = (const struct i2c_config_timing *) i2c_timings_##index, \
242+
.n_timings = ARRAY_SIZE(i2c_timings_##index),)) \
243+
}; \
244+
\
245+
I2C_RTIO_DEFINE(CONCAT(_i2c, index, _stm32_rtio), \
246+
DT_INST_PROP_OR(index, sq_size, CONFIG_I2C_RTIO_SQ_SIZE), \
247+
DT_INST_PROP_OR(index, cq_size, CONFIG_I2C_RTIO_CQ_SIZE)); \
248+
\
249+
static struct i2c_stm32_data i2c_stm32_dev_data_##index = { \
250+
.ctx = &CONCAT(_i2c, index, _stm32_rtio), \
251+
}; \
252+
\
253+
PM_DEVICE_DT_INST_DEFINE(index, i2c_stm32_pm_action); \
254+
\
255+
I2C_DEVICE_DT_INST_DEFINE(index, i2c_stm32_init, \
256+
PM_DEVICE_DT_INST_GET(index), \
257+
&i2c_stm32_dev_data_##index, \
258+
&i2c_stm32_cfg_##index, \
259+
POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \
260+
&api_funcs); \
261+
\
262+
I2C_STM32_IRQ_HANDLER(index)
263+
264+
DT_INST_FOREACH_STATUS_OKAY(I2C_STM32_INIT)

0 commit comments

Comments
 (0)