aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/i2c/controller.rs
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-12-05 14:28:47 +0100
committerJames Munns <[email protected]>2025-12-05 14:28:47 +0100
commitb252db845e19603faf528cf93fe0c44757a27430 (patch)
tree99e646d17bed747df244dd607a15f5a67baa530a /embassy-mcxa/src/i2c/controller.rs
parent6a1eed83b9df8ffa81b93860f530f5bb3252d996 (diff)
Move
Diffstat (limited to 'embassy-mcxa/src/i2c/controller.rs')
-rw-r--r--embassy-mcxa/src/i2c/controller.rs455
1 files changed, 455 insertions, 0 deletions
diff --git a/embassy-mcxa/src/i2c/controller.rs b/embassy-mcxa/src/i2c/controller.rs
new file mode 100644
index 000000000..41bbc821d
--- /dev/null
+++ b/embassy-mcxa/src/i2c/controller.rs
@@ -0,0 +1,455 @@
1//! LPI2C controller driver
2
3use core::marker::PhantomData;
4
5use embassy_hal_internal::Peri;
6use mcxa_pac::lpi2c0::mtdr::Cmd;
7
8use super::{Blocking, Error, Instance, Mode, Result, SclPin, SdaPin};
9use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig};
10use crate::clocks::{enable_and_reset, PoweredClock};
11use crate::AnyPin;
12
13/// Bus speed (nominal SCL, no clock stretching)
14#[derive(Clone, Copy, Default, PartialEq)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16pub enum Speed {
17 #[default]
18 /// 100 kbit/sec
19 Standard,
20 /// 400 kbit/sec
21 Fast,
22 /// 1 Mbit/sec
23 FastPlus,
24 /// 3.4 Mbit/sec
25 UltraFast,
26}
27
28impl From<Speed> for (u8, u8, u8, u8) {
29 fn from(value: Speed) -> (u8, u8, u8, u8) {
30 match value {
31 Speed::Standard => (0x3d, 0x37, 0x3b, 0x1d),
32 Speed::Fast => (0x0e, 0x0c, 0x0d, 0x06),
33 Speed::FastPlus => (0x04, 0x03, 0x03, 0x02),
34
35 // UltraFast is "special". Leaving it unimplemented until
36 // the driver and the clock API is further stabilized.
37 Speed::UltraFast => todo!(),
38 }
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44enum SendStop {
45 No,
46 Yes,
47}
48
49/// I2C controller configuration
50#[derive(Clone, Copy, Default)]
51#[non_exhaustive]
52pub struct Config {
53 /// Bus speed
54 pub speed: Speed,
55}
56
57/// I2C Controller Driver.
58pub struct I2c<'d, T: Instance, M: Mode> {
59 _peri: Peri<'d, T>,
60 _scl: Peri<'d, AnyPin>,
61 _sda: Peri<'d, AnyPin>,
62 _phantom: PhantomData<M>,
63 is_hs: bool,
64}
65
66impl<'d, T: Instance> I2c<'d, T, Blocking> {
67 /// Create a new blocking instance of the I2C Controller bus driver.
68 pub fn new_blocking(
69 peri: Peri<'d, T>,
70 scl: Peri<'d, impl SclPin<T>>,
71 sda: Peri<'d, impl SdaPin<T>>,
72 config: Config,
73 ) -> Result<Self> {
74 Self::new_inner(peri, scl, sda, config)
75 }
76}
77
78impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
79 fn new_inner(
80 _peri: Peri<'d, T>,
81 scl: Peri<'d, impl SclPin<T>>,
82 sda: Peri<'d, impl SdaPin<T>>,
83 config: Config,
84 ) -> Result<Self> {
85 let (power, source, div) = Self::clock_config(config.speed);
86
87 // Enable clocks
88 let conf = Lpi2cConfig {
89 power,
90 source,
91 div,
92 instance: T::CLOCK_INSTANCE,
93 };
94
95 _ = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
96
97 scl.mux();
98 sda.mux();
99
100 let _scl = scl.into();
101 let _sda = sda.into();
102
103 Self::set_config(&config)?;
104
105 Ok(Self {
106 _peri,
107 _scl,
108 _sda,
109 _phantom: PhantomData,
110 is_hs: config.speed == Speed::UltraFast,
111 })
112 }
113
114 fn set_config(config: &Config) -> Result<()> {
115 // Disable the controller.
116 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().disabled()));
117
118 // Soft-reset the controller, read and write FIFOs.
119 critical_section::with(|_| {
120 T::regs()
121 .mcr()
122 .modify(|_, w| w.rst().reset().rtf().reset().rrf().reset());
123 // According to Reference Manual section 40.7.1.4, "There
124 // is no minimum delay required before clearing the
125 // software reset", therefore we clear it immediately.
126 T::regs().mcr().modify(|_, w| w.rst().not_reset());
127
128 T::regs().mcr().modify(|_, w| w.dozen().clear_bit().dbgen().clear_bit());
129 });
130
131 let (clklo, clkhi, sethold, datavd) = config.speed.into();
132
133 critical_section::with(|_| {
134 T::regs().mccr0().modify(|_, w| unsafe {
135 w.clklo()
136 .bits(clklo)
137 .clkhi()
138 .bits(clkhi)
139 .sethold()
140 .bits(sethold)
141 .datavd()
142 .bits(datavd)
143 })
144 });
145
146 // Enable the controller.
147 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().enabled()));
148
149 // Clear all flags
150 T::regs().msr().write(|w| {
151 w.epf()
152 .clear_bit_by_one()
153 .sdf()
154 .clear_bit_by_one()
155 .ndf()
156 .clear_bit_by_one()
157 .alf()
158 .clear_bit_by_one()
159 .fef()
160 .clear_bit_by_one()
161 .pltf()
162 .clear_bit_by_one()
163 .dmf()
164 .clear_bit_by_one()
165 .stf()
166 .clear_bit_by_one()
167 });
168
169 Ok(())
170 }
171
172 // REVISIT: turn this into a function of the speed parameter
173 fn clock_config(speed: Speed) -> (PoweredClock, Lpi2cClockSel, Div4) {
174 match speed {
175 Speed::Standard | Speed::Fast | Speed::FastPlus => (
176 PoweredClock::NormalEnabledDeepSleepDisabled,
177 Lpi2cClockSel::FroLfDiv,
178 const { Div4::no_div() },
179 ),
180 Speed::UltraFast => (
181 PoweredClock::NormalEnabledDeepSleepDisabled,
182 Lpi2cClockSel::FroHfDiv,
183 const { Div4::no_div() },
184 ),
185 }
186 }
187
188 fn is_tx_fifo_full(&mut self) -> bool {
189 let txfifo_size = 1 << T::regs().param().read().mtxfifo().bits();
190 T::regs().mfsr().read().txcount().bits() == txfifo_size
191 }
192
193 fn is_tx_fifo_empty(&mut self) -> bool {
194 T::regs().mfsr().read().txcount() == 0
195 }
196
197 fn is_rx_fifo_empty(&mut self) -> bool {
198 T::regs().mfsr().read().rxcount() == 0
199 }
200
201 fn status(&mut self) -> Result<()> {
202 // Wait for TxFIFO to be drained
203 while !self.is_tx_fifo_empty() {}
204
205 let msr = T::regs().msr().read();
206 T::regs().msr().write(|w| {
207 w.epf()
208 .clear_bit_by_one()
209 .sdf()
210 .clear_bit_by_one()
211 .ndf()
212 .clear_bit_by_one()
213 .alf()
214 .clear_bit_by_one()
215 .fef()
216 .clear_bit_by_one()
217 .fef()
218 .clear_bit_by_one()
219 .pltf()
220 .clear_bit_by_one()
221 .dmf()
222 .clear_bit_by_one()
223 .stf()
224 .clear_bit_by_one()
225 });
226
227 if msr.ndf().bit_is_set() {
228 Err(Error::AddressNack)
229 } else if msr.alf().bit_is_set() {
230 Err(Error::ArbitrationLoss)
231 } else {
232 Ok(())
233 }
234 }
235
236 fn send_cmd(&mut self, cmd: Cmd, data: u8) {
237 #[cfg(feature = "defmt")]
238 defmt::trace!(
239 "Sending cmd '{}' ({}) with data '{:08x}' MSR: {:08x}",
240 cmd,
241 cmd as u8,
242 data,
243 T::regs().msr().read().bits()
244 );
245
246 T::regs()
247 .mtdr()
248 .write(|w| unsafe { w.data().bits(data) }.cmd().variant(cmd));
249 }
250
251 fn start(&mut self, address: u8, read: bool) -> Result<()> {
252 if address >= 0x80 {
253 return Err(Error::AddressOutOfRange(address));
254 }
255
256 // Wait until we have space in the TxFIFO
257 while self.is_tx_fifo_full() {}
258
259 let addr_rw = address << 1 | if read { 1 } else { 0 };
260 self.send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw);
261
262 // Check controller status
263 self.status()
264 }
265
266 fn stop(&mut self) -> Result<()> {
267 // Wait until we have space in the TxFIFO
268 while self.is_tx_fifo_full() {}
269
270 self.send_cmd(Cmd::Stop, 0);
271 self.status()
272 }
273
274 fn blocking_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> {
275 self.start(address, true)?;
276
277 if read.is_empty() {
278 return Err(Error::InvalidReadBufferLength);
279 }
280
281 for chunk in read.chunks_mut(256) {
282 // Wait until we have space in the TxFIFO
283 while self.is_tx_fifo_full() {}
284
285 self.send_cmd(Cmd::Receive, (chunk.len() - 1) as u8);
286
287 for byte in chunk.iter_mut() {
288 // Wait until there's data in the RxFIFO
289 while self.is_rx_fifo_empty() {}
290
291 *byte = T::regs().mrdr().read().data().bits();
292 }
293
294 if send_stop == SendStop::Yes {
295 self.stop()?;
296 }
297 }
298
299 Ok(())
300 }
301
302 fn blocking_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> {
303 self.start(address, false)?;
304
305 // Usually, embassy HALs error out with an empty write,
306 // however empty writes are useful for writing I2C scanning
307 // logic through write probing. That is, we send a start with
308 // R/w bit cleared, but instead of writing any data, just send
309 // the stop onto the bus. This has the effect of checking if
310 // the resulting address got an ACK but causing no
311 // side-effects to the device on the other end.
312 //
313 // Because of this, we are not going to error out in case of
314 // empty writes.
315 #[cfg(feature = "defmt")]
316 if write.is_empty() {
317 defmt::trace!("Empty write, write probing?");
318 }
319
320 for byte in write {
321 // Wait until we have space in the TxFIFO
322 while self.is_tx_fifo_full() {}
323
324 self.send_cmd(Cmd::Transmit, *byte);
325 }
326
327 if send_stop == SendStop::Yes {
328 self.stop()?;
329 }
330
331 Ok(())
332 }
333
334 // Public API: Blocking
335
336 /// Read from address into buffer blocking caller until done.
337 pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<()> {
338 self.blocking_read_internal(address, read, SendStop::Yes)
339 // Automatic Stop
340 }
341
342 /// Write to address from buffer blocking caller until done.
343 pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<()> {
344 self.blocking_write_internal(address, write, SendStop::Yes)
345 }
346
347 /// Write to address from bytes and read from address into buffer blocking caller until done.
348 pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
349 self.blocking_write_internal(address, write, SendStop::No)?;
350 self.blocking_read_internal(address, read, SendStop::Yes)
351 }
352}
353
354impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {
355 type Error = Error;
356
357 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<()> {
358 self.blocking_read(address, buffer)
359 }
360}
361
362impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {
363 type Error = Error;
364
365 fn write(&mut self, address: u8, bytes: &[u8]) -> Result<()> {
366 self.blocking_write(address, bytes)
367 }
368}
369
370impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {
371 type Error = Error;
372
373 fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<()> {
374 self.blocking_write_read(address, bytes, buffer)
375 }
376}
377
378impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> {
379 type Error = Error;
380
381 fn exec(&mut self, address: u8, operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>]) -> Result<()> {
382 if let Some((last, rest)) = operations.split_last_mut() {
383 for op in rest {
384 match op {
385 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
386 self.blocking_read_internal(address, buf, SendStop::No)?
387 }
388 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
389 self.blocking_write_internal(address, buf, SendStop::No)?
390 }
391 }
392 }
393
394 match last {
395 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
396 self.blocking_read_internal(address, buf, SendStop::Yes)
397 }
398 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
399 self.blocking_write_internal(address, buf, SendStop::Yes)
400 }
401 }
402 } else {
403 Ok(())
404 }
405 }
406}
407
408impl embedded_hal_1::i2c::Error for Error {
409 fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
410 match *self {
411 Self::ArbitrationLoss => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
412 Self::AddressNack => {
413 embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
414 }
415 _ => embedded_hal_1::i2c::ErrorKind::Other,
416 }
417 }
418}
419
420impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {
421 type Error = Error;
422}
423
424impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
425 fn transaction(&mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> {
426 if let Some((last, rest)) = operations.split_last_mut() {
427 for op in rest {
428 match op {
429 embedded_hal_1::i2c::Operation::Read(buf) => {
430 self.blocking_read_internal(address, buf, SendStop::No)?
431 }
432 embedded_hal_1::i2c::Operation::Write(buf) => {
433 self.blocking_write_internal(address, buf, SendStop::No)?
434 }
435 }
436 }
437
438 match last {
439 embedded_hal_1::i2c::Operation::Read(buf) => self.blocking_read_internal(address, buf, SendStop::Yes),
440 embedded_hal_1::i2c::Operation::Write(buf) => self.blocking_write_internal(address, buf, SendStop::Yes),
441 }
442 } else {
443 Ok(())
444 }
445 }
446}
447
448impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> {
449 type Config = Config;
450 type ConfigError = Error;
451
452 fn set_config(&mut self, config: &Self::Config) -> Result<()> {
453 Self::set_config(config)
454 }
455}