aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/i2c
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-12-04 18:37:01 +0100
committerJames Munns <[email protected]>2025-12-04 18:43:11 +0100
commitdc6bf5d44675f6f2013ddfab6b14df25a996a965 (patch)
treeab593af8f4b2be502a809833f80e4f3b741b8fad /embassy-mcxa/src/i2c
parent22bae2d80153003af4637c4b0cc26d52858f5228 (diff)
Move to subfolder
Diffstat (limited to 'embassy-mcxa/src/i2c')
-rw-r--r--embassy-mcxa/src/i2c/controller.rs742
-rw-r--r--embassy-mcxa/src/i2c/mod.rs194
2 files changed, 936 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..182a53aea
--- /dev/null
+++ b/embassy-mcxa/src/i2c/controller.rs
@@ -0,0 +1,742 @@
1//! LPI2C controller driver
2
3use core::future::Future;
4use core::marker::PhantomData;
5
6use embassy_hal_internal::drop::OnDrop;
7use embassy_hal_internal::Peri;
8use mcxa_pac::lpi2c0::mtdr::Cmd;
9
10use super::{Async, Blocking, Error, Instance, InterruptHandler, Mode, Result, SclPin, SdaPin};
11use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig};
12use crate::clocks::{enable_and_reset, PoweredClock};
13use crate::interrupt::typelevel::Interrupt;
14use crate::AnyPin;
15
16/// Bus speed (nominal SCL, no clock stretching)
17#[derive(Clone, Copy, Default, PartialEq)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub enum Speed {
20 #[default]
21 /// 100 kbit/sec
22 Standard,
23 /// 400 kbit/sec
24 Fast,
25 /// 1 Mbit/sec
26 FastPlus,
27 /// 3.4 Mbit/sec
28 UltraFast,
29}
30
31impl From<Speed> for (u8, u8, u8, u8) {
32 fn from(value: Speed) -> (u8, u8, u8, u8) {
33 match value {
34 Speed::Standard => (0x3d, 0x37, 0x3b, 0x1d),
35 Speed::Fast => (0x0e, 0x0c, 0x0d, 0x06),
36 Speed::FastPlus => (0x04, 0x03, 0x03, 0x02),
37
38 // UltraFast is "special". Leaving it unimplemented until
39 // the driver and the clock API is further stabilized.
40 Speed::UltraFast => todo!(),
41 }
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47enum SendStop {
48 No,
49 Yes,
50}
51
52/// I2C controller configuration
53#[derive(Clone, Copy, Default)]
54#[non_exhaustive]
55pub struct Config {
56 /// Bus speed
57 pub speed: Speed,
58}
59
60/// I2C Controller Driver.
61pub struct I2c<'d, T: Instance, M: Mode> {
62 _peri: Peri<'d, T>,
63 _scl: Peri<'d, AnyPin>,
64 _sda: Peri<'d, AnyPin>,
65 _phantom: PhantomData<M>,
66 is_hs: bool,
67}
68
69impl<'d, T: Instance> I2c<'d, T, Blocking> {
70 /// Create a new blocking instance of the I2C Controller bus driver.
71 pub fn new_blocking(
72 peri: Peri<'d, T>,
73 scl: Peri<'d, impl SclPin<T>>,
74 sda: Peri<'d, impl SdaPin<T>>,
75 config: Config,
76 ) -> Result<Self> {
77 Self::new_inner(peri, scl, sda, config)
78 }
79}
80
81impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
82 fn new_inner(
83 _peri: Peri<'d, T>,
84 scl: Peri<'d, impl SclPin<T>>,
85 sda: Peri<'d, impl SdaPin<T>>,
86 config: Config,
87 ) -> Result<Self> {
88 let (power, source, div) = Self::clock_config(config.speed);
89
90 // Enable clocks
91 let conf = Lpi2cConfig {
92 power,
93 source,
94 div,
95 instance: T::CLOCK_INSTANCE,
96 };
97
98 _ = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
99
100 scl.mux();
101 sda.mux();
102
103 let _scl = scl.into();
104 let _sda = sda.into();
105
106 Self::set_config(&config)?;
107
108 Ok(Self {
109 _peri,
110 _scl,
111 _sda,
112 _phantom: PhantomData,
113 is_hs: config.speed == Speed::UltraFast,
114 })
115 }
116
117 fn set_config(config: &Config) -> Result<()> {
118 // Disable the controller.
119 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().disabled()));
120
121 // Soft-reset the controller, read and write FIFOs.
122 Self::reset_fifos();
123 critical_section::with(|_| {
124 T::regs().mcr().modify(|_, w| w.rst().reset());
125 // According to Reference Manual section 40.7.1.4, "There
126 // is no minimum delay required before clearing the
127 // software reset", therefore we clear it immediately.
128 T::regs().mcr().modify(|_, w| w.rst().not_reset());
129
130 T::regs().mcr().modify(|_, w| w.dozen().clear_bit().dbgen().clear_bit());
131 });
132
133 let (clklo, clkhi, sethold, datavd) = config.speed.into();
134
135 critical_section::with(|_| {
136 T::regs().mccr0().modify(|_, w| unsafe {
137 w.clklo()
138 .bits(clklo)
139 .clkhi()
140 .bits(clkhi)
141 .sethold()
142 .bits(sethold)
143 .datavd()
144 .bits(datavd)
145 })
146 });
147
148 // Enable the controller.
149 critical_section::with(|_| T::regs().mcr().modify(|_, w| w.men().enabled()));
150
151 // Clear all flags
152 T::regs().msr().write(|w| {
153 w.epf()
154 .clear_bit_by_one()
155 .sdf()
156 .clear_bit_by_one()
157 .ndf()
158 .clear_bit_by_one()
159 .alf()
160 .clear_bit_by_one()
161 .fef()
162 .clear_bit_by_one()
163 .pltf()
164 .clear_bit_by_one()
165 .dmf()
166 .clear_bit_by_one()
167 .stf()
168 .clear_bit_by_one()
169 });
170
171 Ok(())
172 }
173
174 // REVISIT: turn this into a function of the speed parameter
175 fn clock_config(speed: Speed) -> (PoweredClock, Lpi2cClockSel, Div4) {
176 match speed {
177 Speed::Standard | Speed::Fast | Speed::FastPlus => (
178 PoweredClock::NormalEnabledDeepSleepDisabled,
179 Lpi2cClockSel::FroLfDiv,
180 const { Div4::no_div() },
181 ),
182 Speed::UltraFast => (
183 PoweredClock::NormalEnabledDeepSleepDisabled,
184 Lpi2cClockSel::FroHfDiv,
185 const { Div4::no_div() },
186 ),
187 }
188 }
189
190 /// Resets both TX and RX FIFOs dropping their contents.
191 fn reset_fifos() {
192 critical_section::with(|_| {
193 T::regs().mcr().modify(|_, w| w.rtf().reset().rrf().reset());
194 });
195 }
196
197 /// Checks whether the TX FIFO is full
198 fn is_tx_fifo_full() -> bool {
199 let txfifo_size = 1 << T::regs().param().read().mtxfifo().bits();
200 T::regs().mfsr().read().txcount().bits() == txfifo_size
201 }
202
203 /// Checks whether the TX FIFO is empty
204 fn is_tx_fifo_empty() -> bool {
205 T::regs().mfsr().read().txcount() == 0
206 }
207
208 /// Checks whether the RX FIFO is empty.
209 fn is_rx_fifo_empty() -> bool {
210 T::regs().mfsr().read().rxcount() == 0
211 }
212
213 /// Reads and parses the controller status producing an
214 /// appropriate `Result<(), Error>` variant.
215 fn status() -> Result<()> {
216 let msr = T::regs().msr().read();
217 T::regs().msr().write(|w| {
218 w.epf()
219 .clear_bit_by_one()
220 .sdf()
221 .clear_bit_by_one()
222 .ndf()
223 .clear_bit_by_one()
224 .alf()
225 .clear_bit_by_one()
226 .fef()
227 .clear_bit_by_one()
228 .fef()
229 .clear_bit_by_one()
230 .pltf()
231 .clear_bit_by_one()
232 .dmf()
233 .clear_bit_by_one()
234 .stf()
235 .clear_bit_by_one()
236 });
237
238 if msr.ndf().bit_is_set() {
239 Err(Error::AddressNack)
240 } else if msr.alf().bit_is_set() {
241 Err(Error::ArbitrationLoss)
242 } else if msr.fef().bit_is_set() {
243 Err(Error::FifoError)
244 } else {
245 Ok(())
246 }
247 }
248
249 /// Inserts the given command into the outgoing FIFO.
250 ///
251 /// Caller must ensure there is space in the FIFO for the new
252 /// command.
253 fn send_cmd(cmd: Cmd, data: u8) {
254 #[cfg(feature = "defmt")]
255 defmt::trace!(
256 "Sending cmd '{}' ({}) with data '{:08x}' MSR: {:08x}",
257 cmd,
258 cmd as u8,
259 data,
260 T::regs().msr().read().bits()
261 );
262
263 T::regs()
264 .mtdr()
265 .write(|w| unsafe { w.data().bits(data) }.cmd().variant(cmd));
266 }
267
268 /// Prepares an appropriate Start condition on bus by issuing a
269 /// `Start` command together with the device address and R/w bit.
270 ///
271 /// Blocks waiting for space in the FIFO to become available, then
272 /// sends the command and blocks waiting for the FIFO to become
273 /// empty ensuring the command was sent.
274 fn start(&mut self, address: u8, read: bool) -> Result<()> {
275 if address >= 0x80 {
276 return Err(Error::AddressOutOfRange(address));
277 }
278
279 // Wait until we have space in the TxFIFO
280 while Self::is_tx_fifo_full() {}
281
282 let addr_rw = address << 1 | if read { 1 } else { 0 };
283 Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw);
284
285 // Wait for TxFIFO to be drained
286 while !Self::is_tx_fifo_empty() {}
287
288 // Check controller status
289 Self::status()
290 }
291
292 /// Prepares a Stop condition on the bus.
293 ///
294 /// Analogous to `start`, this blocks waiting for space in the
295 /// FIFO to become available, then sends the command and blocks
296 /// waiting for the FIFO to become empty ensuring the command was
297 /// sent.
298 fn stop() -> Result<()> {
299 // Wait until we have space in the TxFIFO
300 while Self::is_tx_fifo_full() {}
301
302 Self::send_cmd(Cmd::Stop, 0);
303
304 // Wait for TxFIFO to be drained
305 while !Self::is_tx_fifo_empty() {}
306
307 Self::status()
308 }
309
310 fn blocking_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> {
311 if read.is_empty() {
312 return Err(Error::InvalidReadBufferLength);
313 }
314
315 for chunk in read.chunks_mut(256) {
316 self.start(address, true)?;
317
318 // Wait until we have space in the TxFIFO
319 while Self::is_tx_fifo_full() {}
320
321 Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8);
322
323 for byte in chunk.iter_mut() {
324 // Wait until there's data in the RxFIFO
325 while Self::is_rx_fifo_empty() {}
326
327 *byte = T::regs().mrdr().read().data().bits();
328 }
329 }
330
331 if send_stop == SendStop::Yes {
332 Self::stop()?;
333 }
334
335 Ok(())
336 }
337
338 fn blocking_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> {
339 self.start(address, false)?;
340
341 // Usually, embassy HALs error out with an empty write,
342 // however empty writes are useful for writing I2C scanning
343 // logic through write probing. That is, we send a start with
344 // R/w bit cleared, but instead of writing any data, just send
345 // the stop onto the bus. This has the effect of checking if
346 // the resulting address got an ACK but causing no
347 // side-effects to the device on the other end.
348 //
349 // Because of this, we are not going to error out in case of
350 // empty writes.
351 #[cfg(feature = "defmt")]
352 if write.is_empty() {
353 defmt::trace!("Empty write, write probing?");
354 }
355
356 for byte in write {
357 // Wait until we have space in the TxFIFO
358 while Self::is_tx_fifo_full() {}
359
360 Self::send_cmd(Cmd::Transmit, *byte);
361 }
362
363 if send_stop == SendStop::Yes {
364 Self::stop()?;
365 }
366
367 Ok(())
368 }
369
370 // Public API: Blocking
371
372 /// Read from address into buffer blocking caller until done.
373 pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<()> {
374 self.blocking_read_internal(address, read, SendStop::Yes)
375 }
376
377 /// Write to address from buffer blocking caller until done.
378 pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<()> {
379 self.blocking_write_internal(address, write, SendStop::Yes)
380 }
381
382 /// Write to address from bytes and read from address into buffer blocking caller until done.
383 pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
384 self.blocking_write_internal(address, write, SendStop::No)?;
385 self.blocking_read_internal(address, read, SendStop::Yes)
386 }
387}
388
389impl<'d, T: Instance> I2c<'d, T, Async> {
390 /// Create a new async instance of the I2C Controller bus driver.
391 pub fn new_async(
392 peri: Peri<'d, T>,
393 scl: Peri<'d, impl SclPin<T>>,
394 sda: Peri<'d, impl SdaPin<T>>,
395 _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
396 config: Config,
397 ) -> Result<Self> {
398 T::Interrupt::unpend();
399
400 // Safety: `_irq` ensures an Interrupt Handler exists.
401 unsafe { T::Interrupt::enable() };
402
403 Self::new_inner(peri, scl, sda, config)
404 }
405
406 fn remediation() {
407 #[cfg(feature = "defmt")]
408 defmt::trace!("Future dropped, issuing stop",);
409
410 // if the FIFO is not empty, drop its contents.
411 if !Self::is_tx_fifo_empty() {
412 Self::reset_fifos();
413 }
414
415 // send a stop command
416 let _ = Self::stop();
417 }
418
419 fn enable_rx_ints(&mut self) {
420 T::regs().mier().write(|w| {
421 w.rdie()
422 .enabled()
423 .ndie()
424 .enabled()
425 .alie()
426 .enabled()
427 .feie()
428 .enabled()
429 .pltie()
430 .enabled()
431 });
432 }
433
434 fn enable_tx_ints(&mut self) {
435 T::regs().mier().write(|w| {
436 w.tdie()
437 .enabled()
438 .ndie()
439 .enabled()
440 .alie()
441 .enabled()
442 .feie()
443 .enabled()
444 .pltie()
445 .enabled()
446 });
447 }
448
449 async fn async_start(&mut self, address: u8, read: bool) -> Result<()> {
450 if address >= 0x80 {
451 return Err(Error::AddressOutOfRange(address));
452 }
453
454 // send the start command
455 let addr_rw = address << 1 | if read { 1 } else { 0 };
456 Self::send_cmd(if self.is_hs { Cmd::StartHs } else { Cmd::Start }, addr_rw);
457
458 T::wait_cell()
459 .wait_for(|| {
460 // enable interrupts
461 self.enable_tx_ints();
462 // if the command FIFO is empty, we're done sending start
463 Self::is_tx_fifo_empty()
464 })
465 .await
466 .map_err(|_| Error::Other)?;
467
468 Self::status()
469 }
470
471 async fn async_stop(&mut self) -> Result<()> {
472 // send the stop command
473 Self::send_cmd(Cmd::Stop, 0);
474
475 T::wait_cell()
476 .wait_for(|| {
477 // enable interrupts
478 self.enable_tx_ints();
479 // if the command FIFO is empty, we're done sending stop
480 Self::is_tx_fifo_empty()
481 })
482 .await
483 .map_err(|_| Error::Other)?;
484
485 Self::status()
486 }
487
488 async fn async_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<()> {
489 if read.is_empty() {
490 return Err(Error::InvalidReadBufferLength);
491 }
492
493 // perform corrective action if the future is dropped
494 let on_drop = OnDrop::new(|| Self::remediation());
495
496 for chunk in read.chunks_mut(256) {
497 self.async_start(address, true).await?;
498
499 // send receive command
500 Self::send_cmd(Cmd::Receive, (chunk.len() - 1) as u8);
501
502 T::wait_cell()
503 .wait_for(|| {
504 // enable interrupts
505 self.enable_tx_ints();
506 // if the command FIFO is empty, we're done sending start
507 Self::is_tx_fifo_empty()
508 })
509 .await
510 .map_err(|_| Error::Other)?;
511
512 for byte in chunk.iter_mut() {
513 T::wait_cell()
514 .wait_for(|| {
515 // enable interrupts
516 self.enable_rx_ints();
517 // if the rx FIFO is not empty, we need to read a byte
518 !Self::is_rx_fifo_empty()
519 })
520 .await
521 .map_err(|_| Error::ReadFail)?;
522
523 *byte = T::regs().mrdr().read().data().bits();
524 }
525 }
526
527 if send_stop == SendStop::Yes {
528 self.async_stop().await?;
529 }
530
531 // defuse it if the future is not dropped
532 on_drop.defuse();
533
534 Ok(())
535 }
536
537 async fn async_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<()> {
538 self.async_start(address, false).await?;
539
540 // perform corrective action if the future is dropped
541 let on_drop = OnDrop::new(|| Self::remediation());
542
543 // Usually, embassy HALs error out with an empty write,
544 // however empty writes are useful for writing I2C scanning
545 // logic through write probing. That is, we send a start with
546 // R/w bit cleared, but instead of writing any data, just send
547 // the stop onto the bus. This has the effect of checking if
548 // the resulting address got an ACK but causing no
549 // side-effects to the device on the other end.
550 //
551 // Because of this, we are not going to error out in case of
552 // empty writes.
553 #[cfg(feature = "defmt")]
554 if write.is_empty() {
555 defmt::trace!("Empty write, write probing?");
556 }
557
558 for byte in write {
559 T::wait_cell()
560 .wait_for(|| {
561 // enable interrupts
562 self.enable_tx_ints();
563 // initiate transmit
564 Self::send_cmd(Cmd::Transmit, *byte);
565 // if the tx FIFO is empty, we're done transmiting
566 Self::is_tx_fifo_empty()
567 })
568 .await
569 .map_err(|_| Error::WriteFail)?;
570 }
571
572 if send_stop == SendStop::Yes {
573 self.async_stop().await?;
574 }
575
576 // defuse it if the future is not dropped
577 on_drop.defuse();
578
579 Ok(())
580 }
581
582 // Public API: Async
583
584 /// Read from address into buffer asynchronously.
585 pub fn async_read<'a>(
586 &mut self,
587 address: u8,
588 read: &'a mut [u8],
589 ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> {
590 self.async_read_internal(address, read, SendStop::Yes)
591 }
592
593 /// Write to address from buffer asynchronously.
594 pub fn async_write<'a>(
595 &mut self,
596 address: u8,
597 write: &'a [u8],
598 ) -> impl Future<Output = Result<()>> + use<'_, 'a, 'd, T> {
599 self.async_write_internal(address, write, SendStop::Yes)
600 }
601
602 /// Write to address from bytes and read from address into buffer asynchronously.
603 pub async fn async_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<()> {
604 self.async_write_internal(address, write, SendStop::No).await?;
605 self.async_read_internal(address, read, SendStop::Yes).await
606 }
607}
608
609impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {
610 type Error = Error;
611
612 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<()> {
613 self.blocking_read(address, buffer)
614 }
615}
616
617impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {
618 type Error = Error;
619
620 fn write(&mut self, address: u8, bytes: &[u8]) -> Result<()> {
621 self.blocking_write(address, bytes)
622 }
623}
624
625impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {
626 type Error = Error;
627
628 fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<()> {
629 self.blocking_write_read(address, bytes, buffer)
630 }
631}
632
633impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> {
634 type Error = Error;
635
636 fn exec(&mut self, address: u8, operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>]) -> Result<()> {
637 if let Some((last, rest)) = operations.split_last_mut() {
638 for op in rest {
639 match op {
640 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
641 self.blocking_read_internal(address, buf, SendStop::No)?
642 }
643 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
644 self.blocking_write_internal(address, buf, SendStop::No)?
645 }
646 }
647 }
648
649 match last {
650 embedded_hal_02::blocking::i2c::Operation::Read(buf) => {
651 self.blocking_read_internal(address, buf, SendStop::Yes)
652 }
653 embedded_hal_02::blocking::i2c::Operation::Write(buf) => {
654 self.blocking_write_internal(address, buf, SendStop::Yes)
655 }
656 }
657 } else {
658 Ok(())
659 }
660 }
661}
662
663impl embedded_hal_1::i2c::Error for Error {
664 fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
665 match *self {
666 Self::ArbitrationLoss => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
667 Self::AddressNack => {
668 embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
669 }
670 _ => embedded_hal_1::i2c::ErrorKind::Other,
671 }
672 }
673}
674
675impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {
676 type Error = Error;
677}
678
679impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
680 fn transaction(&mut self, address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>]) -> Result<()> {
681 if let Some((last, rest)) = operations.split_last_mut() {
682 for op in rest {
683 match op {
684 embedded_hal_1::i2c::Operation::Read(buf) => {
685 self.blocking_read_internal(address, buf, SendStop::No)?
686 }
687 embedded_hal_1::i2c::Operation::Write(buf) => {
688 self.blocking_write_internal(address, buf, SendStop::No)?
689 }
690 }
691 }
692
693 match last {
694 embedded_hal_1::i2c::Operation::Read(buf) => self.blocking_read_internal(address, buf, SendStop::Yes),
695 embedded_hal_1::i2c::Operation::Write(buf) => self.blocking_write_internal(address, buf, SendStop::Yes),
696 }
697 } else {
698 Ok(())
699 }
700 }
701}
702
703impl<'d, T: Instance> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> {
704 async fn transaction(
705 &mut self,
706 address: u8,
707 operations: &mut [embedded_hal_async::i2c::Operation<'_>],
708 ) -> Result<()> {
709 if let Some((last, rest)) = operations.split_last_mut() {
710 for op in rest {
711 match op {
712 embedded_hal_async::i2c::Operation::Read(buf) => {
713 self.async_read_internal(address, buf, SendStop::No).await?
714 }
715 embedded_hal_async::i2c::Operation::Write(buf) => {
716 self.async_write_internal(address, buf, SendStop::No).await?
717 }
718 }
719 }
720
721 match last {
722 embedded_hal_async::i2c::Operation::Read(buf) => {
723 self.async_read_internal(address, buf, SendStop::Yes).await
724 }
725 embedded_hal_async::i2c::Operation::Write(buf) => {
726 self.async_write_internal(address, buf, SendStop::Yes).await
727 }
728 }
729 } else {
730 Ok(())
731 }
732 }
733}
734
735impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> {
736 type Config = Config;
737 type ConfigError = Error;
738
739 fn set_config(&mut self, config: &Self::Config) -> Result<()> {
740 Self::set_config(config)
741 }
742}
diff --git a/embassy-mcxa/src/i2c/mod.rs b/embassy-mcxa/src/i2c/mod.rs
new file mode 100644
index 000000000..9a014224a
--- /dev/null
+++ b/embassy-mcxa/src/i2c/mod.rs
@@ -0,0 +1,194 @@
1//! I2C Support
2
3use core::marker::PhantomData;
4
5use embassy_hal_internal::PeripheralType;
6use maitake_sync::WaitCell;
7use paste::paste;
8
9use crate::clocks::periph_helpers::Lpi2cConfig;
10use crate::clocks::{ClockError, Gate};
11use crate::gpio::{GpioPin, SealedPin};
12use crate::{interrupt, pac};
13
14/// Shorthand for `Result<T>`.
15pub type Result<T> = core::result::Result<T, Error>;
16
17pub mod controller;
18
19/// Error information type
20#[derive(Debug, Copy, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub enum Error {
23 /// Clock configuration error.
24 ClockSetup(ClockError),
25 /// FIFO Error
26 FifoError,
27 /// Reading for I2C failed.
28 ReadFail,
29 /// Writing to I2C failed.
30 WriteFail,
31 /// I2C address NAK condition.
32 AddressNack,
33 /// Bus level arbitration loss.
34 ArbitrationLoss,
35 /// Address out of range.
36 AddressOutOfRange(u8),
37 /// Invalid write buffer length.
38 InvalidWriteBufferLength,
39 /// Invalid read buffer length.
40 InvalidReadBufferLength,
41 /// Other internal errors or unexpected state.
42 Other,
43}
44
45/// I2C interrupt handler.
46pub struct InterruptHandler<T: Instance> {
47 _phantom: PhantomData<T>,
48}
49
50impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
51 unsafe fn on_interrupt() {
52 if T::regs().mier().read().bits() != 0 {
53 T::regs().mier().write(|w| {
54 w.tdie()
55 .disabled()
56 .rdie()
57 .disabled()
58 .epie()
59 .disabled()
60 .sdie()
61 .disabled()
62 .ndie()
63 .disabled()
64 .alie()
65 .disabled()
66 .feie()
67 .disabled()
68 .pltie()
69 .disabled()
70 .dmie()
71 .disabled()
72 .stie()
73 .disabled()
74 });
75
76 T::wait_cell().wake();
77 }
78 }
79}
80
81mod sealed {
82 /// Seal a trait
83 pub trait Sealed {}
84}
85
86impl<T: GpioPin> sealed::Sealed for T {}
87
88trait SealedInstance {
89 fn regs() -> &'static pac::lpi2c0::RegisterBlock;
90 fn wait_cell() -> &'static WaitCell;
91}
92
93/// I2C Instance
94#[allow(private_bounds)]
95pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = Lpi2cConfig> {
96 /// Interrupt for this I2C instance.
97 type Interrupt: interrupt::typelevel::Interrupt;
98 /// Clock instance
99 const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance;
100}
101
102macro_rules! impl_instance {
103 ($($n:expr),*) => {
104 $(
105 paste!{
106 impl SealedInstance for crate::peripherals::[<LPI2C $n>] {
107 fn regs() -> &'static pac::lpi2c0::RegisterBlock {
108 unsafe { &*pac::[<Lpi2c $n>]::ptr() }
109 }
110
111 fn wait_cell() -> &'static WaitCell {
112 static WAIT_CELL: WaitCell = WaitCell::new();
113 &WAIT_CELL
114 }
115 }
116
117 impl Instance for crate::peripherals::[<LPI2C $n>] {
118 type Interrupt = crate::interrupt::typelevel::[<LPI2C $n>];
119 const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance
120 = crate::clocks::periph_helpers::Lpi2cInstance::[<Lpi2c $n>];
121 }
122 }
123 )*
124 };
125}
126
127impl_instance!(0, 1, 2, 3);
128
129/// SCL pin trait.
130pub trait SclPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
131 fn mux(&self);
132}
133
134/// SDA pin trait.
135pub trait SdaPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {
136 fn mux(&self);
137}
138
139/// Driver mode.
140#[allow(private_bounds)]
141pub trait Mode: sealed::Sealed {}
142
143/// Blocking mode.
144pub struct Blocking;
145impl sealed::Sealed for Blocking {}
146impl Mode for Blocking {}
147
148/// Async mode.
149pub struct Async;
150impl sealed::Sealed for Async {}
151impl Mode for Async {}
152
153macro_rules! impl_pin {
154 ($pin:ident, $peri:ident, $fn:ident, $trait:ident) => {
155 impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin {
156 fn mux(&self) {
157 self.set_pull(crate::gpio::Pull::Disabled);
158 self.set_slew_rate(crate::gpio::SlewRate::Fast.into());
159 self.set_drive_strength(crate::gpio::DriveStrength::Double.into());
160 self.set_function(crate::pac::port0::pcr0::Mux::$fn);
161 self.set_enable_input_buffer();
162 }
163 }
164 };
165}
166
167impl_pin!(P0_16, LPI2C0, Mux2, SdaPin);
168impl_pin!(P0_17, LPI2C0, Mux2, SclPin);
169impl_pin!(P0_18, LPI2C0, Mux2, SclPin);
170impl_pin!(P0_19, LPI2C0, Mux2, SdaPin);
171impl_pin!(P1_0, LPI2C1, Mux3, SdaPin);
172impl_pin!(P1_1, LPI2C1, Mux3, SclPin);
173impl_pin!(P1_2, LPI2C1, Mux3, SdaPin);
174impl_pin!(P1_3, LPI2C1, Mux3, SclPin);
175impl_pin!(P1_8, LPI2C2, Mux3, SdaPin);
176impl_pin!(P1_9, LPI2C2, Mux3, SclPin);
177impl_pin!(P1_10, LPI2C2, Mux3, SdaPin);
178impl_pin!(P1_11, LPI2C2, Mux3, SclPin);
179impl_pin!(P1_12, LPI2C1, Mux2, SdaPin);
180impl_pin!(P1_13, LPI2C1, Mux2, SclPin);
181impl_pin!(P1_14, LPI2C1, Mux2, SclPin);
182impl_pin!(P1_15, LPI2C1, Mux2, SdaPin);
183impl_pin!(P1_30, LPI2C0, Mux3, SdaPin);
184impl_pin!(P1_31, LPI2C0, Mux3, SclPin);
185impl_pin!(P3_27, LPI2C3, Mux2, SclPin);
186impl_pin!(P3_28, LPI2C3, Mux2, SdaPin);
187// impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin?
188impl_pin!(P3_30, LPI2C3, Mux2, SclPin);
189impl_pin!(P3_31, LPI2C3, Mux2, SdaPin);
190impl_pin!(P4_2, LPI2C2, Mux2, SdaPin);
191impl_pin!(P4_3, LPI2C0, Mux2, SclPin);
192impl_pin!(P4_4, LPI2C2, Mux2, SdaPin);
193impl_pin!(P4_5, LPI2C0, Mux2, SclPin);
194// impl_pin!(P4_6, LPI2C0, Mux2, HreqPin); What is this HREQ pin?