Skip to main content

ws63_hal/
i2c.rs

1//! I2C master driver for WS63 (I2C0/1, FIFO-capable).
2//!
3//! SCL = I2C_CLK / (2 * (scl_h + scl_l) * 2) where each scl value*2 = actual period.
4//! The I2C clock is the **24 MHz TCXO crystal** ([`crate::soc::ws63::I2C_CLOCK_HZ`]),
5//! NOT the 240 MHz CPU clock: unlike UART/SPI, the vendor `clock_init` leaves I2C on
6//! the crystal (`i2c_port_set_clock_value(REQ_24M)`).
7
8use crate::peripherals::{I2c0, I2c1};
9use core::marker::PhantomData;
10
11pub struct I2c<'d, T> {
12    idx: u8,
13    _peripheral: PhantomData<&'d T>,
14}
15
16fn i2c_regs(idx: u8) -> &'static ws63_pac::i2c0::RegisterBlock {
17    unsafe {
18        match idx {
19            0 => &*I2c0::ptr(),
20            1 => &*I2c1::ptr(),
21            _ => unreachable!(),
22        }
23    }
24}
25
26impl<'d> I2c<'d, I2c0<'d>> {
27    pub fn new_i2c0(_i2c: I2c0<'d>, freq: u32) -> Self {
28        configure_i2c(0, freq);
29        Self { idx: 0, _peripheral: PhantomData }
30    }
31}
32
33impl<'d> I2c<'d, I2c1<'d>> {
34    pub fn new_i2c1(_i2c: I2c1<'d>, freq: u32) -> Self {
35        configure_i2c(1, freq);
36        Self { idx: 1, _peripheral: PhantomData }
37    }
38}
39
40fn configure_i2c(idx: u8, freq: u32) {
41    let r = i2c_regs(idx);
42    let pclk = crate::soc::ws63::I2C_CLOCK_HZ;
43    let freq = if freq == 0 { 1 } else { freq };
44    let period = pclk / (2 * freq);
45    let half = period / 2;
46    r.i2c_scl_h().write(|w| unsafe { w.bits(half) });
47    r.i2c_scl_l().write(|w| unsafe { w.bits(half) });
48    // Enable I2C in FIFO mode with all interrupts unmasked
49    r.i2c_ctrl().write(|w| unsafe {
50        w.bits(0);
51        w.i2c_en().set_bit();
52        w.mode_ctrl().set_bit();
53        // Unmask ACK error interrupt so we can detect NACK
54        w.int_ack_err_mask().set_bit()
55    });
56}
57
58/// Bounded busy-wait. Returns [`I2cError::Timeout`] instead of hanging the CPU
59/// forever when the bus or a peer never drives the expected status bit (mirrors
60/// the bounded waits in `spi.rs`). Previously these were unbounded `while !..{}`
61/// loops that would deadlock the core on a stuck/absent slave.
62const I2C_WAIT_LOOPS: u32 = 1_000_000;
63
64#[inline]
65fn wait_until(mut ready: impl FnMut() -> bool) -> Result<(), I2cError> {
66    let mut n = I2C_WAIT_LOOPS;
67    while !ready() {
68        n -= 1;
69        if n == 0 {
70            return Err(I2cError::Timeout);
71        }
72    }
73    Ok(())
74}
75
76impl<T> I2c<'_, T> {
77    #[allow(dead_code)]
78    fn wait_not_busy(&self) -> Result<(), I2cError> {
79        let r = i2c_regs(self.idx);
80        wait_until(|| !r.i2c_sr().read().bus_busy().bit_is_set())
81    }
82
83    fn clear_interrupts(&self) {
84        let r = i2c_regs(self.idx);
85        // Write 1 to each bit to clear: done, arb_loss, ack_err, rx, tx, stop, start, rxtide, txtide
86        unsafe { r.i2c_icr().write(|w| w.bits(0x7FF)) };
87    }
88
89    /// Check for ACK error (NACK from slave). Reads I2C_SR bit[2] per fbb_ws63.
90    fn check_ack(&self) -> Result<(), I2cError> {
91        let r = i2c_regs(self.idx);
92        if r.i2c_sr().read().int_ack_err().bit_is_set() {
93            return Err(I2cError::Ack);
94        }
95        Ok(())
96    }
97
98    /// Wait for TX ready and check for ACK error.
99    fn wait_tx_ack(&self) -> Result<(), I2cError> {
100        let r = i2c_regs(self.idx);
101        wait_until(|| r.i2c_sr().read().int_tx().bit_is_set())?;
102        // Check ACK after each TX (address and data bytes)
103        self.check_ack()
104    }
105
106    /// Send START + address + R/W bit via I2C_COM register.
107    /// Uses direct write (not RMW) to avoid restoring auto-cleared bits.
108    fn send_start(&self, addr_byte: u32, is_read: bool) -> Result<(), I2cError> {
109        let r = i2c_regs(self.idx);
110        self.clear_interrupts();
111
112        // Load address into TXR
113        r.i2c_txr().write(|w| unsafe { w.bits(addr_byte) });
114
115        // Direct write to COM register (bits[3:0] auto-clear after operation)
116        unsafe {
117            r.i2c_com().write(|w| w.bits(0));
118        }
119        let mut com: u32 = 0;
120        com |= 1 << 3; // op_start
121        com |= 1 << 1; // op_we (write address byte)
122        if is_read {
123            // For read, after START+WE sends address+R, we need START+RD
124            // The direction bit is encoded in addr_byte, so for address+R/W=1
125            // we just send START+WE; the read command comes separately
126        }
127        unsafe {
128            r.i2c_com().write(|w| w.bits(com));
129        }
130
131        self.wait_tx_ack()
132    }
133
134    pub fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), I2cError> {
135        let r = i2c_regs(self.idx);
136
137        // Start + address (R/W=0)
138        self.send_start((addr as u32) << 1, false)?;
139
140        // Write data bytes
141        for &byte in data {
142            r.i2c_txr().write(|w| unsafe { w.bits(byte as u32) });
143            // Direct write to COM: op_we
144            unsafe { r.i2c_com().write(|w| w.bits(1 << 1)) };
145            self.wait_tx_ack()?;
146            self.clear_interrupts();
147        }
148
149        // Stop
150        r.i2c_com().write(|w| w.op_stop().set_bit());
151        wait_until(|| r.i2c_sr().read().int_stop().bit_is_set())?;
152        self.clear_interrupts();
153
154        Ok(())
155    }
156
157    pub fn read(&mut self, addr: u8, buf: &mut [u8]) -> Result<(), I2cError> {
158        let r = i2c_regs(self.idx);
159
160        // Start + address (R/W=1)
161        self.send_start(((addr as u32) << 1) | 1, true)?;
162
163        // Read bytes
164        let buf_len = buf.len();
165        for (i, byte) in buf.iter_mut().enumerate() {
166            let is_last = i == buf_len - 1;
167            // Direct write to COM: op_rd, optionally op_ack (NACK on last byte)
168            let mut com: u32 = 1 << 2; // op_rd
169            if is_last {
170                com |= 1 << 4; // op_ack (host sends NACK on last byte)
171            }
172            unsafe { r.i2c_com().write(|w| w.bits(com)) };
173
174            wait_until(|| r.i2c_sr().read().int_rx().bit_is_set())?;
175            *byte = r.i2c_rxr().read().bits() as u8;
176            self.clear_interrupts();
177        }
178
179        // Stop
180        r.i2c_com().write(|w| w.op_stop().set_bit());
181        wait_until(|| r.i2c_sr().read().int_stop().bit_is_set())?;
182        self.clear_interrupts();
183
184        Ok(())
185    }
186
187    /// Combined write-then-read with repeated START (Sr) between operations,
188    /// matching the I2C specification for register-based device access.
189    pub fn write_read(&mut self, addr: u8, wr_buf: &[u8], rd_buf: &mut [u8]) -> Result<(), I2cError> {
190        let r = i2c_regs(self.idx);
191
192        if !wr_buf.is_empty() {
193            // Start + address (R/W=0)
194            self.send_start((addr as u32) << 1, false)?;
195
196            // Write register address / data bytes
197            for &byte in wr_buf {
198                r.i2c_txr().write(|w| unsafe { w.bits(byte as u32) });
199                unsafe { r.i2c_com().write(|w| w.bits(1 << 1)) }; // op_we
200                self.wait_tx_ack()?;
201                self.clear_interrupts();
202            }
203            // NO STOP here — go directly to repeated START
204        }
205
206        if !rd_buf.is_empty() {
207            // Repeated START + address (R/W=1)
208            self.send_start(((addr as u32) << 1) | 1, true)?;
209
210            let buf_len = rd_buf.len();
211            for (i, byte) in rd_buf.iter_mut().enumerate() {
212                let is_last = i == buf_len - 1;
213                let mut com: u32 = 1 << 2; // op_rd
214                if is_last {
215                    com |= 1 << 4; // op_ack (NACK on last)
216                }
217                unsafe { r.i2c_com().write(|w| w.bits(com)) };
218                wait_until(|| r.i2c_sr().read().int_rx().bit_is_set())?;
219                *byte = r.i2c_rxr().read().bits() as u8;
220                self.clear_interrupts();
221            }
222        }
223
224        // Stop (at end of combined transaction)
225        r.i2c_com().write(|w| w.op_stop().set_bit());
226        wait_until(|| r.i2c_sr().read().int_stop().bit_is_set())?;
227        self.clear_interrupts();
228
229        Ok(())
230    }
231
232    /// Core transaction implementation with repeated START support.
233    ///
234    /// Uses repeated START (Sr) between operations as required by the
235    /// embedded-hal I2c trait contract. Only emits STOP after the last operation.
236    fn transaction_impl(
237        &mut self,
238        address: u8,
239        operations: &mut [embedded_hal::i2c::Operation<'_>],
240    ) -> Result<(), I2cError> {
241        let r = i2c_regs(self.idx);
242        let addr_w = (address as u32) << 1; // R/W=0 for write
243        let addr_r = ((address as u32) << 1) | 1; // R/W=1 for read
244
245        for op in operations.iter_mut() {
246            match op {
247                embedded_hal::i2c::Operation::Write(data) => {
248                    self.send_start(addr_w, false)?;
249                    self.clear_interrupts();
250
251                    for &byte in data.iter() {
252                        r.i2c_txr().write(|w| unsafe { w.bits(byte as u32) });
253                        unsafe { r.i2c_com().write(|w| w.bits(1 << 1)) }; // op_we
254                        self.wait_tx_ack()?;
255                        self.clear_interrupts();
256                    }
257                    // NO STOP between operations — next START will be repeated START
258                }
259                embedded_hal::i2c::Operation::Read(buf) => {
260                    self.send_start(addr_r, true)?;
261                    self.clear_interrupts();
262
263                    let buf_len = buf.len();
264                    for (i, byte) in buf.iter_mut().enumerate() {
265                        let is_last = i == buf_len - 1;
266                        let mut com: u32 = 1 << 2; // op_rd
267                        if is_last {
268                            com |= 1 << 4; // op_ack (host sends NACK on last byte)
269                        }
270                        unsafe { r.i2c_com().write(|w| w.bits(com)) };
271                        wait_until(|| r.i2c_sr().read().int_rx().bit_is_set())?;
272                        *byte = r.i2c_rxr().read().bits() as u8;
273                        self.clear_interrupts();
274                    }
275                    // NO STOP between operations — next START will be repeated START
276                }
277            }
278        }
279
280        // STOP at end of all operations
281        r.i2c_com().write(|w| w.op_stop().set_bit());
282        wait_until(|| r.i2c_sr().read().int_stop().bit_is_set())?;
283        self.clear_interrupts();
284
285        Ok(())
286    }
287}
288
289#[derive(Debug)]
290pub enum I2cError {
291    Ack,
292    BusError,
293    Timeout,
294}
295
296impl embedded_hal::i2c::Error for I2cError {
297    fn kind(&self) -> embedded_hal::i2c::ErrorKind {
298        match self {
299            I2cError::Ack => {
300                embedded_hal::i2c::ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Unknown)
301            }
302            I2cError::BusError => embedded_hal::i2c::ErrorKind::Bus,
303            I2cError::Timeout => embedded_hal::i2c::ErrorKind::Other,
304        }
305    }
306}
307
308// ── embedded-hal I2c trait ──────────────────────────────────────
309
310impl embedded_hal::i2c::ErrorType for I2c<'_, I2c0<'_>> {
311    type Error = I2cError;
312}
313
314impl embedded_hal::i2c::I2c for I2c<'_, I2c0<'_>> {
315    fn transaction(
316        &mut self,
317        address: u8,
318        operations: &mut [embedded_hal::i2c::Operation<'_>],
319    ) -> Result<(), Self::Error> {
320        self.transaction_impl(address, operations)
321    }
322}
323
324impl embedded_hal::i2c::ErrorType for I2c<'_, I2c1<'_>> {
325    type Error = I2cError;
326}
327
328impl embedded_hal::i2c::I2c for I2c<'_, I2c1<'_>> {
329    fn transaction(
330        &mut self,
331        address: u8,
332        operations: &mut [embedded_hal::i2c::Operation<'_>],
333    ) -> Result<(), Self::Error> {
334        self.transaction_impl(address, operations)
335    }
336}
337
338// ── Tests ──────────────────────────────────────────────────────
339
340#[cfg(test)]
341mod tests {
342    #[test]
343    fn test_i2c_address_write_encoding() {
344        // I2C write address = addr << 1 (R/W=0)
345        assert_eq!((0x50u32 << 1), 0xA0);
346        assert_eq!((0x50u32 << 1) & 0xFE, 0xA0); // Write bit is 0
347    }
348
349    #[test]
350    fn test_i2c_address_read_encoding() {
351        // I2C read address = (addr << 1) | 1 (R/W=1)
352        assert_eq!(((0x50u32 << 1) | 1), 0xA1);
353    }
354
355    #[test]
356    fn test_i2c_address_write_read_differ_by_one_bit() {
357        let addr_w = (0x48u32) << 1; // 0x90
358        let addr_r = ((0x48u32) << 1) | 1; // 0x91
359        assert_eq!(addr_r, addr_w | 1);
360        assert_eq!(addr_w & 0x01, 0); // Write bit cleared
361        assert_eq!(addr_r & 0x01, 1); // Read bit set
362    }
363
364    #[test]
365    fn test_i2c_10bit_high_address_encoding() {
366        // 10-bit addressing uses 0x78-0x7B range
367        let addr: u32 = 0x78;
368        let addr_w = addr << 1;
369        assert_eq!(addr_w, 0xF0); // Address fits in 7 bits
370    }
371}
372
373// ── Async I2C (embedded-hal-async) ──────────────────────────────────────────
374// Reuses the blocking transaction (FIFO-paced; synchronous loopback on ws63-qemu).
375#[cfg(feature = "async")]
376mod asynch_impl {
377    use super::{I2c, I2c0, I2c1};
378    use embedded_hal::i2c::Operation;
379
380    macro_rules! async_i2c {
381        ($inst:ty) => {
382            impl embedded_hal_async::i2c::I2c for I2c<'_, $inst> {
383                async fn transaction(&mut self, addr: u8, ops: &mut [Operation<'_>]) -> Result<(), Self::Error> {
384                    embedded_hal::i2c::I2c::transaction(self, addr, ops)
385                }
386            }
387        };
388    }
389    async_i2c!(I2c0<'_>);
390    async_i2c!(I2c1<'_>);
391}