Skip to content

Commit edf102c

Browse files
committed
wip: zephyr: i2c support
Start of i2c support, including using rtio for it. Signed-off-by: David Brown <[email protected]>
1 parent b63dc72 commit edf102c

File tree

8 files changed

+339
-0
lines changed

8 files changed

+339
-0
lines changed

docgen/prj.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ CONFIG_GPIO=y
77
CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT=y
88
CONFIG_PRINTK=y
99
CONFIG_POLL=y
10+
CONFIG_I2C=y
11+
CONFIG_I2C_RTIO=y
12+
CONFIG_RTIO=y

dt-rust.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,21 @@
9898
- type: reg
9999
device: "crate::device::flash::FlashPartition"
100100

101+
# I2C.
102+
- name: i2c
103+
rules:
104+
- type: compatible
105+
value:
106+
names:
107+
- "snps,designware-i2c"
108+
level: 0
109+
actions:
110+
- type: instance
111+
value:
112+
raw:
113+
type: myself
114+
device: crate::device::i2c::I2C
115+
101116
# Generate a pseudo node that matches all of the labels across the tree with their nodes.
102117
- name: labels
103118
rules:

zephyr-sys/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,13 @@ fn main() -> Result<()> {
7676
.derive_copy(false)
7777
.allowlist_function("k_.*")
7878
.allowlist_function("gpio_.*")
79+
.allowlist_function("i2c_.*")
7980
.allowlist_function("flash_.*")
8081
.allowlist_function("zr_.*")
82+
.allowlist_function("mpsc_.*")
83+
.allowlist_function("rtio.*")
8184
.allowlist_item("GPIO_.*")
85+
.allowlist_item("I2C_.*")
8286
.allowlist_item("FLASH_.*")
8387
.allowlist_item("Z_.*")
8488
.allowlist_item("ZR_.*")

zephyr-sys/wrapper.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ extern int errno;
4343
#include <zephyr/bluetooth/bluetooth.h>
4444
#include <zephyr/drivers/flash.h>
4545
#include <zephyr/irq.h>
46+
#include <zephyr/drivers/i2c.h>
47+
#include <zephyr/rtio/rtio.h>
48+
#include <zephyr/sys/mpsc_lockfree.h>
4649

4750
/*
4851
* bindgen will only output #defined constants that resolve to simple numbers. These are some
@@ -63,6 +66,12 @@ const uint32_t ZR_GPIO_INT_MODE_DISABLE_ONLY = GPIO_INT_MODE_DISABLE_ONLY;
6366
const uint32_t ZR_GPIO_INT_MODE_ENABLE_ONLY = GPIO_INT_MODE_ENABLE_ONLY;
6467
#endif
6568

69+
const uint8_t ZR_I2C_MSG_WRITE = I2C_MSG_WRITE;
70+
const uint8_t ZR_I2C_MSG_READ = I2C_MSG_READ;
71+
const uint8_t ZR_I2C_MSG_STOP = I2C_MSG_STOP;
72+
73+
const uint16_t ZR_RTIO_SQE_NO_RESPONSE = RTIO_SQE_NO_RESPONSE;
74+
6675
/*
6776
* Zephyr's irq_lock() and irq_unlock() are macros not inline functions, so we need some inlines to
6877
* access them.

zephyr/src/device.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::sync::atomic::{AtomicBool, Ordering};
1212

1313
pub mod flash;
1414
pub mod gpio;
15+
pub mod i2c;
1516

1617
// Allow dead code, because it isn't required for a given build to have any devices.
1718
/// Device uniqueness.

zephyr/src/device/i2c.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//! Zpehyr I2C interface
2+
3+
use core::{ffi::c_int, marker::PhantomData};
4+
5+
use crate::{error::to_result, printkln, raw};
6+
7+
use super::{NoStatic, Unique};
8+
9+
/// A single I2C controller.
10+
pub struct I2C {
11+
/// The underlying device itself.
12+
#[allow(dead_code)]
13+
pub(crate) device: *const raw::device,
14+
}
15+
16+
unsafe impl Send for I2C {}
17+
18+
impl I2C {
19+
/// Constructor, used by the devicetree generated code.
20+
#[allow(dead_code)]
21+
pub(crate) unsafe fn new(
22+
unique: &Unique,
23+
_data: &'static NoStatic,
24+
device: *const raw::device,
25+
) -> Option<Self> {
26+
if !unique.once() {
27+
return None;
28+
}
29+
Some(I2C { device })
30+
}
31+
32+
/// Do a write/read.
33+
pub fn write_read(&mut self, write: &[u8], read: &mut [u8]) -> crate::Result<c_int> {
34+
let mut msg = [
35+
raw::i2c_msg {
36+
buf: write.as_ptr() as *mut _,
37+
len: write.len() as u32,
38+
flags: raw::ZR_I2C_MSG_WRITE,
39+
},
40+
raw::i2c_msg {
41+
buf: read.as_mut_ptr(),
42+
len: read.len() as u32,
43+
flags: raw::ZR_I2C_MSG_READ | raw::ZR_I2C_MSG_STOP,
44+
},
45+
];
46+
let res = unsafe {
47+
to_result(
48+
raw::i2c_transfer(
49+
self.device,
50+
msg.as_mut_ptr(),
51+
2,
52+
0x42,
53+
))
54+
};
55+
56+
printkln!("res: {} {}", msg[1].len, msg[1].flags);
57+
58+
res
59+
}
60+
61+
/// Add an i2c operation to the RTIO.
62+
///
63+
/// TODO: Unclear how to indicate that the buffers must live long enough for the submittion.
64+
/// As it is, this is actually completely unsound.
65+
pub fn rtio_write_read(&mut self, write: &[u8], read: &mut [u8]) -> crate::Result<()> {
66+
let _msg = [
67+
raw::i2c_msg {
68+
buf: write.as_ptr() as *mut _,
69+
len: write.len() as u32,
70+
flags: raw::ZR_I2C_MSG_WRITE,
71+
},
72+
raw::i2c_msg {
73+
buf: read.as_mut_ptr(),
74+
len: read.len() as u32,
75+
flags: raw::ZR_I2C_MSG_READ | raw::ZR_I2C_MSG_STOP,
76+
},
77+
];
78+
79+
todo!()
80+
}
81+
}
82+
83+
/// An i2c transaction.
84+
pub struct ReadWrite<'a> {
85+
_phantom: PhantomData<&'a ()>,
86+
msgs: [raw::i2c_msg; 2],
87+
}
88+
89+
impl<'a> ReadWrite<'a> {
90+
/// Construct a new read/write transaction.
91+
pub fn new(write: &'a [u8], read: &'a mut [u8]) -> Self {
92+
Self {
93+
_phantom: PhantomData,
94+
msgs: [
95+
raw::i2c_msg {
96+
buf: write.as_ptr() as *mut _,
97+
len: write.len() as u32,
98+
flags: raw::ZR_I2C_MSG_WRITE,
99+
},
100+
raw::i2c_msg {
101+
buf: read.as_mut_ptr(),
102+
len: read.len() as u32,
103+
flags: raw::ZR_I2C_MSG_READ | raw::ZR_I2C_MSG_STOP,
104+
},
105+
],
106+
}
107+
}
108+
}

zephyr/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ pub mod embassy;
7575
pub mod error;
7676
pub mod logging;
7777
pub mod object;
78+
#[cfg(CONFIG_RTIO)]
79+
pub mod rtio;
7880
#[cfg(CONFIG_RUST_ALLOC)]
7981
pub mod simpletls;
8082
pub mod sync;
@@ -124,6 +126,9 @@ pub mod devicetree {
124126
125127
// Don't enforce doc comments on the generated device tree.
126128
#![allow(missing_docs)]
129+
// Allow nodes to have non-snake-case names. This comes from addresses in the node names, which
130+
// usually use uppercase.
131+
#![allow(non_snake_case)]
127132

128133
include!(concat!(env!("OUT_DIR"), "/devicetree.rs"));
129134
}

zephyr/src/rtio.rs

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
//! Interface to Zephyr 'rtio' infrastructure.
2+
3+
use core::ffi::c_void;
4+
5+
use crate::error::to_result_void;
6+
use crate::raw;
7+
use crate::object::{ObjectInit, ZephyrObject};
8+
9+
/// The underlying structure, holding the rtio, it's semaphores, and pools.
10+
///
11+
/// Note that putting these together in a single struct makes this "pleasant" to use from Rust, but
12+
/// does make the end result incompatible with userspace.
13+
#[repr(C)]
14+
pub struct RtioData<const SQE_SZ: usize, const CQE_SZ: usize> {
15+
/// The overall rtio struct.
16+
rtio: raw::rtio,
17+
/// Sempahore used for the submission queue.
18+
#[cfg(CONFIG_RTIO_SUBMIT_SEM)]
19+
submit_sem: raw::k_sem,
20+
/// Semaphore used for the consumption queue.
21+
#[cfg(CONFIG_RTIO_CONSUME_SEM)]
22+
consume_sem: raw::k_sem,
23+
/// The SQE items.
24+
sqe_pool_items: [raw::rtio_iodev_sqe; SQE_SZ],
25+
/// The SQE pool itself.
26+
sqe_pool: raw::rtio_sqe_pool,
27+
/// The CQE items.
28+
cqe_pool_items: [raw::rtio_cqe; CQE_SZ],
29+
/// The pool of CQEs.
30+
cqe_pool: raw:: rtio_cqe_pool,
31+
}
32+
33+
/// Init based reference to the the underlying rtio object.
34+
///
35+
/// Note that this declaration will _not_ support userspace currently, as the object will not be
36+
/// placed in an iterable linker section. Also, the linker sevction will not work as the
37+
/// ZephyrObject will have an attached atomic used to ensure proper initialization.
38+
pub struct RtioObject<const SQE_SZ: usize, const CQE_SZ: usize>(pub(crate) ZephyrObject<RtioData<SQE_SZ, CQE_SZ>>);
39+
40+
unsafe impl<const SQE_SZ: usize, const CQE_SZ: usize> Sync for RtioObject<SQE_SZ, CQE_SZ> {}
41+
42+
impl<const SQE_SZ: usize, const CQE_SZ: usize> RtioObject<SQE_SZ, CQE_SZ> {
43+
/// Construct a new RTIO pool.
44+
///
45+
/// Create a new RTIO object. These objects generally need to be statically allocated.
46+
pub const fn new() -> Self {
47+
let this = <ZephyrObject<RtioData<SQE_SZ, CQE_SZ>>>::new_raw();
48+
RtioObject(this)
49+
}
50+
51+
/// Acquire a submission object.
52+
pub fn sqe_acquire(&'static self) -> Option<Sqe> {
53+
let this = unsafe { self.0.get() };
54+
55+
let ptr = unsafe {
56+
raw::rtio_sqe_acquire(&raw mut (*this).rtio)
57+
};
58+
59+
if ptr.is_null() {
60+
None
61+
} else {
62+
Some(Sqe { item: ptr })
63+
}
64+
}
65+
66+
/// Submit the work.
67+
pub fn submit(&'static self, wait: usize) -> crate::Result<()> {
68+
let this = unsafe { self.0.get() };
69+
70+
unsafe {
71+
to_result_void(raw::rtio_submit(&raw mut (*this).rtio, wait as u32))
72+
}
73+
}
74+
75+
/// Consume a single completion.
76+
///
77+
/// Will return the completion if available. If returned, it will be released upon drop.
78+
pub fn cqe_consume(&'static self) -> Option<Cqe> {
79+
let this = unsafe { self.0.get() };
80+
81+
let ptr = unsafe {
82+
raw::rtio_cqe_consume(&raw mut (*this).rtio)
83+
};
84+
85+
if ptr.is_null() {
86+
None
87+
} else {
88+
Some(Cqe {
89+
item: ptr,
90+
rtio: unsafe { &raw mut (*this).rtio },
91+
})
92+
}
93+
}
94+
}
95+
96+
impl<const SQE_SZ: usize, const CQE_SZ: usize> ObjectInit<RtioData<SQE_SZ, CQE_SZ>> for ZephyrObject<RtioData<SQE_SZ, CQE_SZ>> {
97+
fn init(item: *mut RtioData<SQE_SZ, CQE_SZ>) {
98+
#[cfg(CONFIG_RTIO_SUBMIT_SEM)]
99+
unsafe {
100+
raw::k_sem_init(&raw mut (*item).submit_sem, 0, raw::K_SEM_MAX_LIMIT);
101+
(*item).rtio.submit_sem = &raw mut (*item).submit_sem;
102+
(*item).rtio.submit_count = 0;
103+
}
104+
#[cfg(CONFIG_RTIO_CONSUME_SEM)]
105+
unsafe {
106+
raw::k_sem_init(&raw mut (*item).consume_sem, 0, raw::K_SEM_MAX_LIMIT);
107+
(*item).rtio.consume_sem = &raw mut (*item).consume_sem;
108+
}
109+
unsafe {
110+
// TODO: Zephyr atomic init?
111+
(*item).rtio.cq_count = 0;
112+
(*item).rtio.xcqcnt = 0;
113+
114+
// Set up the sqe pool.
115+
raw::mpsc_init(&raw mut (*item).sqe_pool.free_q);
116+
(*item).sqe_pool.pool_size = SQE_SZ as u16;
117+
(*item).sqe_pool.pool_free = SQE_SZ as u16;
118+
(*item).sqe_pool.pool = (*item).sqe_pool_items.as_mut_ptr();
119+
120+
for p in &mut (*item).sqe_pool_items {
121+
raw::mpsc_push(&raw mut (*item).sqe_pool.free_q, &raw mut p.q);
122+
}
123+
124+
// Set up the cqe pool
125+
raw::mpsc_init(&raw mut (*item).cqe_pool.free_q);
126+
(*item).cqe_pool.pool_size = CQE_SZ as u16;
127+
(*item).cqe_pool.pool_free = CQE_SZ as u16;
128+
(*item).cqe_pool.pool = (*item).cqe_pool_items.as_mut_ptr();
129+
130+
for p in &mut (*item).cqe_pool_items {
131+
raw::mpsc_push(&raw mut (*item).cqe_pool.free_q, &raw mut p.q);
132+
}
133+
134+
(*item).rtio.sqe_pool = &raw mut (*item).sqe_pool;
135+
(*item).rtio.cqe_pool = &raw mut (*item).cqe_pool;
136+
137+
raw::mpsc_init(&raw mut (*item).rtio.sq);
138+
raw::mpsc_init(&raw mut (*item).rtio.cq);
139+
}
140+
}
141+
}
142+
143+
/// A single Sqe.
144+
///
145+
/// TODO: How to bind the lifetime to the Rtio meaningfully, even though it is all static.
146+
pub struct Sqe {
147+
item: *mut raw::rtio_sqe,
148+
}
149+
150+
impl Sqe {
151+
/// Configure this SQE as a callback.
152+
pub fn prep_callback(&mut self, callback: raw::rtio_callback_t, arg0: *mut c_void, userdata: *mut c_void) {
153+
unsafe {
154+
raw::rtio_sqe_prep_callback(self.item, callback, arg0, userdata);
155+
}
156+
}
157+
158+
/// Configure this SQE as a nop.
159+
pub fn prep_nop(&mut self, dev: *mut raw::rtio_iodev, userdata: *mut c_void) {
160+
unsafe {
161+
raw::rtio_sqe_prep_nop(self.item, dev, userdata);
162+
}
163+
}
164+
165+
/// Add flags.
166+
pub fn or_flags(&mut self, flags: u16) {
167+
unsafe {
168+
(*self.item).flags |= flags;
169+
}
170+
}
171+
}
172+
173+
/// A single Cqe.
174+
pub struct Cqe {
175+
item: *mut raw::rtio_cqe,
176+
rtio: *mut raw::rtio,
177+
}
178+
179+
impl Cqe {
180+
/// Retrieve the result of this operation.
181+
pub fn result(&self) -> i32 {
182+
unsafe {
183+
(*self.item).result
184+
}
185+
}
186+
}
187+
188+
impl Drop for Cqe {
189+
fn drop(&mut self) {
190+
unsafe {
191+
raw::rtio_cqe_release(self.rtio, self.item);
192+
}
193+
}
194+
}

0 commit comments

Comments
 (0)