imxrt-hal 0.5.0

Hardware abstraction layer for NXP i.MX RT microcontrollers.
Documentation
//! Low-power inter-integrated circuit.
//!
//! The `Lpi2c` driver implements all embedded-hal I2C traits. Use these traits to perform
//! common I2C I/O. The driver also exposes lower-level APIs for the LPI2C controller.
//!
//! # Example
//!
//! Demonstrates how to create an LPI2C peripheral, and perform a write-read with
//! a device. This example skips the LPI2C clock configuration. To understand LPI2C
//! clock configuration, see the [`ccm::lpi2c_clk`](crate::ccm::lpi2c_clk) documentation.
//!
//! ```no_run
//! use imxrt_hal as hal;
//! use imxrt_ral as ral;
//! use hal::lpi2c::{self, Lpi2c};
//! use ral::{ccm::CCM, lpi2c::LPI2C3};
//! use eh02::blocking::i2c::WriteRead;
//!
//! let mut pads = // Handle to all processor pads...
//!     # unsafe { imxrt_iomuxc::imxrt1060::Pads::new() };
//!
//! # || -> Option<()> {
//! let mut ccm = unsafe { CCM::instance() };
//! let mut i2c3 = unsafe { LPI2C3::instance() };
//!
//! # const LPI2C_CLK_HZ: u32 = 8_000_000;
//! const LPI2C_400KHz: lpi2c::Timing = lpi2c::Timing::ideal(LPI2C_CLK_HZ, lpi2c::ClockSpeed::KHz400);
//!
//! let mut i2c3 = Lpi2c::new(
//!     i2c3,
//!     lpi2c::Pins {
//!         scl: pads.gpio_ad_b1.p07,
//!         sda: pads.gpio_ad_b1.p06,
//!     },
//!     &LPI2C_400KHz,
//! );
//!
//! let mut input = [0; 3];
//! let output = [0x74];
//! # const MY_DEVICE_ADDRESS: u8 = 0;
//!
//! i2c3.write_read(MY_DEVICE_ADDRESS, &output, &mut input).ok()?;
//!
//! // Release the driver components...
//! let (i2c3, pins) = i2c3.release();
//!
//! // Re-construct without pins...
//! let mut i2c3 = Lpi2c::without_pins(i2c3, &LPI2C_400KHz);
//! # Some(()) }();
//! ```
//!
//! # Limitations
//!
//! This driver supports standard, fast, and fast+ modes. High speed mode is not
//! yet supported, and supporting the mode was not considered in the initial driver
//! design.
use crate::iomuxc::consts;

use crate::iomuxc::lpi2c;
use crate::ral;
use eh02::blocking::i2c as blocking;

/// Data direction.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
    /// Transmit direction (leaving the peripheral).
    Tx,
    /// Receive direction (entering the peripheral).
    Rx,
}

/// LPI2C pins.
pub struct Pins<SCL, SDA>
where
    SCL: lpi2c::Pin<Signal = lpi2c::Scl>,
    SDA: lpi2c::Pin<Signal = lpi2c::Sda, Module = SCL::Module>,
{
    /// Serial clock.
    pub scl: SCL,
    /// Serial data.
    pub sda: SDA,
}

/// An LPI2C driver.
///
/// Use this driver to communicate with I2C devices. This driver
/// implements various `embedded-hal` I2C traits, and you should
/// prefer these implementations for their ease of use.
///
/// See the [module-level documentation](crate::lpi2c) for an example
/// of how to construct this driver.
pub struct Lpi2c<P, const N: u8> {
    lpi2c: ral::lpi2c::Instance<N>,
    pins: P,
}

impl<SCL, SDA, const N: u8> Lpi2c<Pins<SCL, SDA>, N>
where
    SCL: lpi2c::Pin<Signal = lpi2c::Scl, Module = consts::Const<N>>,
    SDA: lpi2c::Pin<Signal = lpi2c::Sda, Module = consts::Const<N>>,
{
    /// Create an LPI2C driver from an LPI2C instance and a pair of pins.
    ///
    /// When this call returns, the LPI2C pins are configured for their
    /// LPI2C functions, the controller is enabled after reset, and the driver
    /// is using the provided timing configuration for the clock.
    pub fn new(
        lpi2c: crate::ral::lpi2c::Instance<N>,
        mut pins: Pins<SCL, SDA>,
        timings: &Timing,
    ) -> Self {
        lpi2c::prepare(&mut pins.scl);
        lpi2c::prepare(&mut pins.sda);
        Self::init(lpi2c, pins, timings)
    }
}

impl<const N: u8> Lpi2c<(), N> {
    /// Create an I2C driver from an LPI2C instance.
    ///
    /// This is similar to [`new()`](Self::new), but it does not configure pins.
    /// You're responsible for configuring pins, and for making sure
    /// the pin configuration doesn't change while this driver is in use.
    pub fn without_pins(lpi2c: ral::lpi2c::Instance<N>, timings: &Timing) -> Self {
        Self::init(lpi2c, (), timings)
    }
}

impl<P, const N: u8> Lpi2c<P, N> {
    /// The peripheral instance.
    pub const N: u8 = N;

    fn init(mut lpi2c: ral::lpi2c::Instance<N>, pins: P, timings: &Timing) -> Self {
        ral::write_reg!(ral::lpi2c, lpi2c, MCR, RST: RST_1);
        while ral::read_reg!(ral::lpi2c, lpi2c, MCR, RST == RST_1) {
            ral::write_reg!(ral::lpi2c, lpi2c, MCR, RST: RST_0);
        }

        // I2C disabled due to reset.
        set_timings(&mut lpi2c, timings);

        ral::write_reg!(ral::lpi2c, lpi2c, MFCR, RXWATER: 0b01, TXWATER: 0b01);
        ral::write_reg!(ral::lpi2c, lpi2c, MCR, MEN: MEN_1);

        Lpi2c { lpi2c, pins }
    }

    /// Indicates if the controller is (`true`) or is not (`false`) enabled.
    pub fn is_controller_enabled(&self) -> bool {
        ral::read_reg!(ral::lpi2c, self.lpi2c, MCR, MEN == MEN_1)
    }

    /// Enable (`true`) or disable (`false`) the controller.
    pub fn set_controller_enable(&mut self, enable: bool) {
        ral::modify_reg!(ral::lpi2c, self.lpi2c, MCR, MEN: enable as u32)
    }

    /// Reset the controller.
    ///
    /// Note that this may not not reset all peripheral state, like the controller
    /// enabled state.
    pub fn reset_controller(&mut self) {
        ral::modify_reg!(ral::lpi2c, self.lpi2c, MCR, RST: RST_1);
        while ral::read_reg!(ral::lpi2c, self.lpi2c, MCR, RST == RST_1) {
            ral::modify_reg!(ral::lpi2c, self.lpi2c, MCR, RST: RST_0);
        }
    }

    /// Release the LPI2C components.
    ///
    /// This does not change any component state; it releases the components as-is.
    /// If you need to obtain the registers in a known, good state, consider calling
    /// methods like [`reset_controller()`](Self::reset_controller) before releasing
    /// the registers.
    pub fn release(self) -> (ral::lpi2c::Instance<N>, P) {
        (self.lpi2c, self.pins)
    }

    /// Read the controller status bits.
    #[inline]
    pub fn controller_status(&self) -> ControllerStatus {
        ControllerStatus::from_bits_truncate(ral::read_reg!(ral::lpi2c, self.lpi2c, MSR))
    }

    /// Clear the controller status bits that are set high.
    ///
    /// The implementation will clear any read-only bits, so it's OK to pass in
    /// `ControllerStatus::all()`.
    #[inline]
    pub fn clear_controller_status(&self, status: ControllerStatus) {
        let msr = status & ControllerStatus::W1C;
        ral::write_reg!(ral::lpi2c, self.lpi2c, MSR, msr.bits());
    }

    /// Resets the transmit and receive FIFOs.
    #[inline(always)]
    pub fn clear_fifo(&mut self) {
        ral::modify_reg!(ral::lpi2c, self.lpi2c, MCR, RRF: RRF_1, RTF: RTF_1);
    }

    /// Enqueue a command into the controller transmit data register.
    ///
    /// `enqueue_controller_command` does not check that the FIFO can hold the
    /// command. Check for the transmit data flag in the status
    /// response to understand the FIFO's state.
    #[inline]
    pub fn enqueue_controller_command(&self, command: ControllerCommand) {
        ral::write_reg!(ral::lpi2c, self.lpi2c, MTDR, command.raw());
    }

    /// Read the controller receive data register.
    ///
    /// Returns `None` if there is no data in the receive FIFO.
    #[inline]
    pub fn read_data_register(&self) -> Option<u8> {
        let (empty, data) = ral::read_reg!(ral::lpi2c, self.lpi2c, MRDR, RXEMPTY, DATA);
        if empty != 0 {
            None
        } else {
            Some(data as u8)
        }
    }

    /// Temporarily disable the LPI2C peripheral.
    ///
    /// The handle to a [`Disabled`](crate::lpi2c::Disabled) driver lets you modify
    /// LPI2C settings that require a fully disabled peripheral.
    pub fn disabled<R>(&mut self, func: impl FnOnce(&mut Disabled<N>) -> R) -> R {
        let mut disabled = Disabled::new(&mut self.lpi2c);
        func(&mut disabled)
    }

    /// If the bus is busy, return the status flags in the error
    /// position.
    fn check_busy(&self) -> Result<(), ControllerStatus> {
        let status = self.controller_status();
        if status.is_bus_busy() {
            Err(status)
        } else {
            Ok(())
        }
    }

    /// Block until there's space in the transmit FIFO..
    ///
    /// Return errors if detected.
    fn wait_for_transmit(&self) -> Result<(), ControllerStatus> {
        loop {
            let status = self.controller_status();
            if status.has_error() {
                return Err(status);
            } else if status.contains(ControllerStatus::TRANSMIT_DATA) {
                return Ok(());
            }
        }
    }

    /// Block until receiving a byte of data.
    ///
    /// Returns errors if detected.
    fn wait_for_data(&self) -> Result<u8, ControllerStatus> {
        loop {
            let status = self.controller_status();
            if status.has_error() {
                return Err(status);
            } else if let Some(data) = self.read_data_register() {
                return Ok(data);
            }
        }
    }

    /// Wait for the end of packet, which happens for STOP or repeated
    /// START conditions.
    ///
    /// Returns errors if detected. This will not unblock for a non-repeated
    /// START.
    fn wait_for_end_of_packet(&self) -> Result<(), ControllerStatus> {
        loop {
            let status = self.controller_status();
            if status.has_error() {
                return Err(status);
            } else if status.contains(ControllerStatus::END_PACKET) {
                return Ok(());
            }
        }
    }

    /// Borrow the pins.
    pub fn pins(&self) -> &P {
        &self.pins
    }

    /// Exclusively borrow the pins.
    pub fn pins_mut(&mut self) -> &mut P {
        &mut self.pins
    }

    /// Returns the bitflags that indicate enabled or disabled LPI2C interrupts.
    #[inline]
    pub fn interrupts(&self) -> Interrupts {
        let raw = ral::read_reg!(ral::lpi2c, self.lpi2c, MIER);
        let interrupts = Interrupts::from_bits_truncate(raw);
        interrupts ^ Interrupts::FIFO_ERROR
    }

    /// Enable or disable LPI2C interrupts.
    #[inline]
    pub fn set_interrupts(&self, interrupts: Interrupts) {
        let interrupts = interrupts ^ Interrupts::FIFO_ERROR;
        ral::write_reg!(ral::lpi2c, self.lpi2c, MIER, interrupts.bits());
    }

    /// Returns the watermark level for the given direction.
    #[inline]
    pub fn watermark(&self, direction: Direction) -> u8 {
        (match direction {
            Direction::Rx => ral::read_reg!(ral::lpi2c, self.lpi2c, MFCR, RXWATER),
            Direction::Tx => ral::read_reg!(ral::lpi2c, self.lpi2c, MFCR, TXWATER),
        }) as u8
    }

    /// Returns the FIFO status.
    #[inline]
    pub fn controller_fifo_status(&self) -> ControllerFifoStatus {
        let (rxcount, txcount) = ral::read_reg!(ral::lpi2c, self.lpi2c, MFSR, RXCOUNT, TXCOUNT);
        ControllerFifoStatus {
            rxcount: rxcount as u16,
            txcount: txcount as u16,
        }
    }
}

/// The number of words in each FIFO.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ControllerFifoStatus {
    /// Number of words in the receive FIFO.
    pub rxcount: u16,
    /// Number of words in the transmit FIFO.
    pub txcount: u16,
}

/// Must be called only when the LPI2C peripheral is disabled.
fn set_timings<const N: u8>(lpi2c: &mut ral::lpi2c::Instance<N>, timings: &Timing) {
    let clock_config = timings.clock_configuration();
    ral::write_reg!(ral::lpi2c, lpi2c, MCCR0,
        CLKHI: clock_config.clkhi as u32,
        CLKLO: clock_config.clklo as u32,
        SETHOLD: clock_config.sethold as u32,
        DATAVD: clock_config.datavd as u32
    );
    ral::modify_reg!(ral::lpi2c, lpi2c, MCFGR1, PRESCALE: timings.prescaler() as u32);

    ral::modify_reg!(ral::lpi2c, lpi2c, MCFGR2,
        FILTSDA: clock_config.filtsda as u32,
        FILTSCL: clock_config.filtscl as u32,
        BUSIDLE: timings.busidle
    );
}

/// A temporarily disabled LPI2C peripheral.
///
/// This handle lets you modify LPI2C settings that require
/// a disabled peripheral.
pub struct Disabled<'a, const N: u8> {
    lpi2c: &'a mut ral::lpi2c::Instance<N>,
    men: bool,
}

impl<'a, const N: u8> Disabled<'a, N> {
    fn new(lpi2c: &'a mut ral::lpi2c::Instance<N>) -> Self {
        let men = ral::read_reg!(ral::lpi2c, lpi2c, MCR, MEN == MEN_1);
        ral::modify_reg!(ral::lpi2c, lpi2c, MCR, MEN: MEN_0);
        Self { lpi2c, men }
    }

    /// Modify LPI2C timing parameters.
    ///
    /// This call only affects parameters used in standard, fast, and fast+ modes.
    /// There is no support for switching into high-speed mode.
    pub fn set_timings(&mut self, timings: &Timing) {
        set_timings(self.lpi2c, timings);
    }

    /// Set the watermark level for a given direction.
    ///
    /// Returns the watermark level committed to the hardware. This may be different
    /// than the supplied `watermark`, since it's limited by the hardware.
    ///
    /// When `direction == Direction::Rx`, the receive data flag is set whenever the
    /// number of words in the receive FIFO is greater than `watermark`.
    ///
    /// When `direction == Direction::Tx`, the transmit data flag is set whenever the
    /// the number of words in the transmit FIFO is less than, or equal, to `watermark`.
    #[inline]
    pub fn set_watermark(&mut self, direction: Direction, watermark: u8) -> u8 {
        let max_watermark = match direction {
            Direction::Rx => 1 << ral::read_reg!(ral::lpi2c, self.lpi2c, PARAM, MRXFIFO),
            Direction::Tx => 1 << ral::read_reg!(ral::lpi2c, self.lpi2c, PARAM, MTXFIFO),
        };

        let watermark = watermark.min(max_watermark - 1);

        match direction {
            Direction::Rx => {
                ral::modify_reg!(ral::lpi2c, self.lpi2c, MFCR, RXWATER: watermark as u32)
            }
            Direction::Tx => {
                ral::modify_reg!(ral::lpi2c, self.lpi2c, MFCR, TXWATER: watermark as u32)
            }
        }

        watermark
    }
}

impl<const N: u8> Drop for Disabled<'_, N> {
    fn drop(&mut self) {
        ral::modify_reg!(ral::lpi2c, self.lpi2c, MCR, MEN: self.men as u32);
    }
}

bitflags::bitflags! {
    /// Status flags for the LPI2C controller.
    pub struct ControllerStatus : u32 {
        /// Bus busy flag.
        ///
        /// If high, the bus is busy.
        const BUS_BUSY = 1 << 25;
        /// Controller busy flag.
        ///
        /// If high, the controller is already active.
        const CONTROLLER_BUSY = 1 << 24;

        //
        // Begin W1C bits.
        //

        /// Data match flag.
        const DATA_MATCH = 1 << 14;
        /// Pin low timeout flag.
        const PIN_LOW_TIMEOUT = 1 << 13;
        /// FIFO error flag.
        const FIFO_ERROR = 1 << 12;
        /// Arbitration lost flag.
        const ARBITRATION_LOST = 1 << 11;
        /// NACK detected flag.
        const NACK_DETECTED = 1 << 10;
        /// STOP detected flag.
        const STOP_DETECTED = 1 << 9;
        /// End packet flag.
        const END_PACKET = 1 << 8;

        //
        // End W1C bits
        //

        /// Receive data flag.
        const RECEIVE_DATA = 1 << 1;
        /// Transmit data flag.
        const TRANSMIT_DATA = 1 << 0;
    }
}

impl ControllerStatus {
    /// Mask of write-1-clear bits.
    const W1C: Self = Self::from_bits_truncate(
        Self::DATA_MATCH.bits()
            | Self::PIN_LOW_TIMEOUT.bits()
            | Self::FIFO_ERROR.bits()
            | Self::ARBITRATION_LOST.bits()
            | Self::NACK_DETECTED.bits()
            | Self::STOP_DETECTED.bits()
            | Self::END_PACKET.bits(),
    );

    /// Bits that definitely indicate an error.
    const ERRORS: Self = Self::from_bits_truncate(
        Self::PIN_LOW_TIMEOUT.bits()
            | Self::FIFO_ERROR.bits()
            | Self::ARBITRATION_LOST.bits()
            | Self::NACK_DETECTED.bits(),
    );

    /// The status indicates that the bus is busy, and it's not
    /// because of us.
    const fn is_bus_busy(self) -> bool {
        self.contains(Self::BUS_BUSY) && !self.contains(Self::CONTROLLER_BUSY)
    }

    /// Indicates if this tatus has any error bits set.
    const fn has_error(self) -> bool {
        self.intersects(Self::ERRORS)
    }
}

bitflags::bitflags! {
    /// LPI2C interrupt settings.
    ///
    /// A set bit indicates that an interrupt is enabled.
    pub struct Interrupts : u32 {
        /// Data match interrupt enable.
        const DATA_MATCH = 1 << 14;
        /// Pin low timout interrupt enable.
        const PIN_LOW_TIMEOUT = 1 << 13;
        /// FIFO error interrupt enable.
        const FIFO_ERROR = 1 << 12;

        //
        // Note: according to the reference manual and SVD files,
        // FEIE is enabled when low, and disabled when high. This
        // is inconsist with all other bits. To handle the difference,
        // This driver flips the bit in order to provide a consistent
        // behavior in software.
        //

        /// Arbitration lost interrupt enable.
        const ARBITRATION_LOST = 1 << 11;
        /// NACK detect interrupt enable.
        const NACK_DETECT = 1 << 10;
        /// STOP detect interrupt enable.
        const STOP_DETECT = 1 << 9;
        /// End packet interrupt enable.
        const END_PACKET = 1 << 8;
        /// Receive data interrupt enable.
        const RECEIVE_DATA = 1 << 1;
        /// Transmit data interrupt enable.
        const TRANSMIT_DATA = 1 << 0;
    }
}

/// (N)ACK responses.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Response {
    /// Response is acknowledge.
    Ack,
    /// Response is not acknowledge.
    Nack,
}

/// LPI2C controller commands.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ControllerCommand {
    /// A transmit command.
    Transmit {
        /// The data byte to enqueue.
        byte: u8,
    },
    /// A receive command.
    Receive {
        /// How many bytes to receive.
        count: u8,
    },
    /// Generate a STOP condition.
    Stop,
    /// Receive and discard.
    ReceiveAndDiscard {
        /// How many bytes to receive and drop.
        drop: u8,
    },
    /// Generate a (repeated) start, transmit the
    /// address `addr`, and expect the `expect` response
    /// from a device.
    Start {
        /// The device you're addressing.
        ///
        /// You're responsible for shifting the address, and setting
        /// the read/write bit. Consider using the `read()` and `write()`
        /// methods for this purpose.
        addr: u8,
        /// The expected response from the device.
        expect: Response,
    },
}

impl ControllerCommand {
    /// Creates a (repeat) start command that describes a read
    /// from a device with address `addr`.
    ///
    /// The expected response is ACK.
    #[inline]
    pub const fn read(addr: u8) -> Self {
        Self::Start {
            addr: (addr << 1) | 1,
            expect: Response::Ack,
        }
    }

    /// Creates a (repeat) start command that describes a write
    /// to a device with address `addr`.
    ///
    /// The expected response is ACK.
    #[inline]
    pub const fn write(addr: u8) -> Self {
        Self::Start {
            addr: addr << 1,
            expect: Response::Ack,
        }
    }
}

impl ControllerCommand {
    /// Turn a command into its raw representation, permitting
    /// 32-bit writes to the transmit data register.
    const fn raw(self) -> u32 {
        use crate::ral::lpi2c::MTDR::CMD::{offset as OFFSET, RW::*};
        match self {
            Self::Transmit { byte } => (CMD_0 << OFFSET) | byte as u32,
            Self::Receive { count } => (CMD_1 << OFFSET) | count.saturating_sub(1) as u32,
            Self::Stop => CMD_2 << OFFSET,
            Self::ReceiveAndDiscard { drop } => (CMD_3 << OFFSET) | drop.saturating_sub(1) as u32,
            Self::Start {
                expect: Response::Ack,
                addr,
            } => (CMD_4 << OFFSET) | addr as u32,
            Self::Start {
                expect: Response::Nack,
                addr,
            } => (CMD_5 << OFFSET) | addr as u32,
        }
    }
}

//
// embedded-hal implementations.
//

impl<P, const N: u8> blocking::TransactionalIter for Lpi2c<P, N> {
    type Error = ControllerStatus;
    fn exec_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error>
    where
        O: IntoIterator<Item = blocking::Operation<'a>>,
    {
        let mut runner = transaction::Runner::new(self)?;
        for mut operation in operations {
            runner.next_operation(address, &mut operation)?;
        }
        runner.stop()?;
        Ok(())
    }
}

impl<P, const N: u8> blocking::WriteIter for Lpi2c<P, N> {
    type Error = ControllerStatus;
    fn write<B>(&mut self, address: u8, bytes: B) -> Result<(), Self::Error>
    where
        B: IntoIterator<Item = u8>,
    {
        blocking::WriteIterRead::write_iter_read(self, address, bytes, &mut [])
    }
}

impl<P, const N: u8> blocking::WriteIterRead for Lpi2c<P, N> {
    type Error = ControllerStatus;
    fn write_iter_read<B>(
        &mut self,
        address: u8,
        bytes: B,
        buffer: &mut [u8],
    ) -> Result<(), Self::Error>
    where
        B: IntoIterator<Item = u8>,
    {
        self.check_busy()?;
        self.clear_fifo();
        self.clear_controller_status(ControllerStatus::W1C);

        self.wait_for_transmit()?;
        self.enqueue_controller_command(ControllerCommand::write(address));

        let cmds = bytes
            .into_iter()
            .map(|byte| ControllerCommand::Transmit { byte });
        for cmd in cmds {
            self.wait_for_transmit()?;
            self.enqueue_controller_command(cmd);
        }

        if !buffer.is_empty() {
            self.wait_for_transmit()?;
            self.enqueue_controller_command(ControllerCommand::read(address));

            self.wait_for_transmit()?;
            self.enqueue_controller_command(ControllerCommand::Receive {
                count: buffer.len() as u8,
            });

            for slot in buffer {
                *slot = self.wait_for_data()?;
            }
        }

        self.wait_for_transmit()?;
        self.enqueue_controller_command(ControllerCommand::Stop);
        self.wait_for_end_of_packet()?;

        Ok(())
    }
}

impl<P, const N: u8> blocking::Transactional for Lpi2c<P, N> {
    type Error = ControllerStatus;
    fn exec<'a>(
        &mut self,
        address: u8,
        operations: &mut [blocking::Operation<'a>],
    ) -> Result<(), Self::Error> {
        let mut runner = transaction::Runner::new(self)?;
        for operation in operations {
            runner.next_operation(address, operation)?;
        }
        runner.stop()?;
        Ok(())
    }
}

impl<P, const N: u8> blocking::Read for Lpi2c<P, N> {
    type Error = ControllerStatus;
    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
        blocking::Transactional::exec(self, address, &mut [blocking::Operation::Read(buffer)])
    }
}

impl<P, const N: u8> blocking::Write for Lpi2c<P, N> {
    type Error = ControllerStatus;
    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
        blocking::Transactional::exec(self, address, &mut [blocking::Operation::Write(bytes)])
    }
}

impl<P, const N: u8> blocking::WriteRead for Lpi2c<P, N> {
    type Error = ControllerStatus;
    fn write_read(
        &mut self,
        address: u8,
        bytes: &[u8],
        buffer: &mut [u8],
    ) -> Result<(), Self::Error> {
        blocking::Transactional::exec(
            self,
            address,
            &mut [
                blocking::Operation::Write(bytes),
                blocking::Operation::Read(buffer),
            ],
        )
    }
}

/// Details for the embedded-hal Transaction
/// implementation.
mod transaction {

    use super::{ControllerCommand, ControllerStatus, Direction, Lpi2c};
    use eh02::blocking::i2c::Operation;

    /// A stateful type that can run I2C operations.
    pub struct Runner<'a, I> {
        lpi2c: &'a mut I,
        direction: Option<Direction>,
    }

    impl<'a, P, const N: u8> Runner<'a, Lpi2c<P, N>> {
        /// Create a new transaction runner.
        ///
        /// Returns an error if the LPI2C is busy.
        pub fn new(lpi2c: &'a mut Lpi2c<P, N>) -> Result<Self, ControllerStatus> {
            lpi2c.check_busy()?;
            lpi2c.clear_fifo();
            lpi2c.clear_controller_status(ControllerStatus::W1C);
            Ok(Self {
                lpi2c,
                direction: None,
            })
        }

        /// Execute the next I2C operation.
        pub fn next_operation(
            &mut self,
            address: u8,
            operation: &mut Operation,
        ) -> Result<(), ControllerStatus> {
            // Quotes throughout indicate the embedded-hal Transactional requirements.
            match (&self.direction, &operation) {
                // "Data from adjacent operations of the same type are sent after each other without an SP or SR."
                (Some(Direction::Tx), Operation::Write(_)) => {}
                (Some(Direction::Rx), Operation::Read(_)) => {}
                // -----------------------------------------------
                // "Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate."
                // "Between adjacent operations of a different type an SR and SAD+R/W is sent."
                (Some(Direction::Rx) | None, Operation::Write(_)) => {
                    self.lpi2c.wait_for_transmit()?;
                    self.lpi2c
                        .enqueue_controller_command(ControllerCommand::write(address));
                }
                (Some(Direction::Tx) | None, Operation::Read(_)) => {
                    self.lpi2c.wait_for_transmit()?;
                    self.lpi2c
                        .enqueue_controller_command(ControllerCommand::read(address));
                }
            };

            match operation {
                Operation::Write(buffer) => {
                    for byte in *buffer {
                        self.lpi2c.wait_for_transmit()?;
                        self.lpi2c
                            .enqueue_controller_command(ControllerCommand::Transmit {
                                byte: *byte,
                            });
                    }
                    self.direction = Some(Direction::Tx);
                }
                // "If the last operation is a Read the peripheral does not send an acknowledge for the last byte."
                //
                // Handled by the hardware. See section 47.3.2.1 in the 1060 RM (rev2).
                Operation::Read(buffer) => {
                    if !buffer.is_empty() {
                        self.lpi2c.wait_for_transmit()?;
                        self.lpi2c
                            .enqueue_controller_command(ControllerCommand::Receive {
                                count: buffer.len() as u8,
                            });
                        for slot in buffer.iter_mut() {
                            *slot = self.lpi2c.wait_for_data()?;
                        }
                    }
                    self.direction = Some(Direction::Rx);
                }
            };

            Ok(())
        }

        /// Send a stop command, and finish the transaction.
        pub fn stop(self) -> Result<(), ControllerStatus> {
            self.lpi2c.wait_for_transmit()?;
            self.lpi2c
                .enqueue_controller_command(ControllerCommand::Stop);
            self.lpi2c.wait_for_end_of_packet()?;
            Ok(())
        }
    }
}

/// Clock configuration fields.
///
/// These fields are written directly to the clock configuration register.
/// All values are written as-is to the register fields. Values that are
/// less than eight bits are truncated by the implementation. You're
/// responsible for making sure that these parameters meet their timing
/// parameter restrictions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ClockConfiguration {
    /// Clock high period.
    ///
    /// Minimum number of cycles that the SCL clock is driven high.
    pub clkhi: u8,
    /// Clock low period.
    ///
    /// Minimum number of cycles that the SCL clock is driven low.
    pub clklo: u8,
    /// Setup hold delay.
    ///
    /// Minimum number of cycles that's used for
    /// - START condition hold
    /// - repeated START setup & hold
    /// - START condition setup
    pub sethold: u8,
    /// Data valid delay.
    ///
    /// Minimum number of cycles for SDA data hold. Must be less than
    /// the minimum SCL low period.
    pub datavd: u8,
    /// Glitch filter SDA.
    ///
    /// Only four bits large. Value of zero represents "no filter," and
    /// non-zero values represent filtered cycles.
    pub filtsda: u8,
    /// Glitch filter for SCL.
    ///
    /// Only four bits large. Value of zero represents "no filter," and
    /// non-zero values represent filtered cycles.
    pub filtscl: u8,
}

/// Source clock prescaler.
///
/// Affects all timing, except for the glitch filters.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Prescaler {
    /// Divide the source clock by 1.
    Prescaler1,
    /// Divide the source clock by 2.
    Prescaler2,
    /// Divide the source clock by 4.
    Prescaler4,
    /// Divide the source clock by 8.
    Prescaler8,
    /// Divide the source clock by 16.
    Prescaler16,
    /// Divide the source clock by 32.
    Prescaler32,
    /// Divide the source clock by 64.
    Prescaler64,
    /// Divide the source clock by 128.
    Prescaler128,
}

impl Prescaler {
    /// Returns the divider value for this prescaler.
    ///
    /// `Prescaler8` produces the value '8'.
    pub const fn divider(self) -> u8 {
        1 << self as u8
    }
}

const _: () = assert!(Prescaler::Prescaler1.divider() == 1);
const _: () = assert!(Prescaler::Prescaler128.divider() == 128);

/// Clock speed.
#[derive(Clone, Copy, Debug)]
pub enum ClockSpeed {
    /// 100 KHz.
    KHz100,
    /// 400 KHz.
    KHz400,
    /// 1 MHz.
    MHz1,
}

impl ClockSpeed {
    const fn frequency(self) -> u32 {
        match self {
            ClockSpeed::KHz100 => 100_000,
            ClockSpeed::KHz400 => 400_000,
            ClockSpeed::MHz1 => 1_000_000,
        }
    }
}

/// LPI2C timing parameters.
///
/// The implementation computes BUSIDLE based on the clock configuration values,
/// but you can override this after construction.
///
/// The simplest way to construct a `Timing` is to use [`ideal()`](Timing::ideal).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Timing {
    clock_configuration: ClockConfiguration,
    prescaler: Prescaler,
    busidle: u32,
}

/// Computes SCL and SDA latency cycles.
///
/// See table 47.3 in the reference manual. `risetime` is an estimate of the number
/// of clock cycles for the line to rise. Ideally, this is zero.
const fn line_latency_cycles(filter: u8, risetime: u8, prescaler: Prescaler) -> u8 {
    (2 + filter + risetime) / prescaler.divider()
}

/// Assuming that CLKHI = 2 * CLKLO = CLK, compute CLK. Saturate at u8::MAX.
///
/// BAUD = (HZ / (2 ^ PRESCALER)) / (3CLK + 2 + SCL_LATENCY)
///
/// Solve for CLK:
///
/// CLK = (HZ / (2 ^ PRESCALER) / BAUD - SCL_LATENCY - 2) / 3
///
/// Keep in mind that CLKHI and CLKLO are 6 bit fields, so the saturation value is still
/// out of range.
const fn compute_clk(hz: u32, baud: ClockSpeed, prescaler: Prescaler, scl_latency: u8) -> u8 {
    let clk: u32 =
        (hz / prescaler.divider() as u32 / baud.frequency() - scl_latency as u32 - 2) / 3;
    if clk > 0xFF {
        0xFFu8
    } else {
        clk as u8
    }
}

impl Timing {
    /// Compute timing parameters assuming an ideal I2C bus.
    ///
    /// This constructor assumes that
    ///
    /// - the SDA / SCL rise times are negligible (take less than one functional clock cycle).
    /// - there's no need for glitch filters (FLITSCL = FILTSDA = 0).
    ///
    /// These assumptions may not hold true for high clock speeds and I2C bus loadings.
    /// If that's the case, you may find it's better to define timing parameters yourself.
    ///
    /// Note that this function can run at compile time. Consider evaluating in a const
    /// context to avoid the possibility of panics.
    ///
    /// # Panics
    ///
    /// After evaluating all prescalars, this function panics if the computed clock period
    /// cannot be represented in the 6 bits available for the configuration.
    pub const fn ideal(clock_hz: u32, clock_speed: ClockSpeed) -> Self {
        const PRESCALERS: [Prescaler; 8] = [
            Prescaler::Prescaler1,
            Prescaler::Prescaler2,
            Prescaler::Prescaler4,
            Prescaler::Prescaler8,
            Prescaler::Prescaler16,
            Prescaler::Prescaler32,
            Prescaler::Prescaler64,
            Prescaler::Prescaler128,
        ];
        /// 6 bits available for all clock configurations.
        const CLOCK_PERIOD_MAX_VAL: u8 = 0x3Fu8;

        // Can't write a for loop in a const function...
        let mut clk = 0xFFu8;
        let mut idx = 0usize;
        while idx < PRESCALERS.len() {
            // Assuming no filters and rise times less than one clock cycle.
            let scl_latency = line_latency_cycles(0, 0, PRESCALERS[idx]);
            clk = compute_clk(clock_hz, clock_speed, PRESCALERS[idx], scl_latency);
            if clk.saturating_mul(2) <= CLOCK_PERIOD_MAX_VAL {
                break;
            }
            idx += 1;
        }

        assert!(
            clk.saturating_mul(2) <= CLOCK_PERIOD_MAX_VAL,
            "Could not compute CLKHI / CLKLO"
        );
        let prescaler = PRESCALERS[idx];

        let mut clkhi = clk;
        if clkhi < 0x01 {
            clkhi = 0x01;
        }

        let mut clklo = clk * 2;
        if clklo < 0x03 {
            clklo = 0x03;
        }

        // No need to assert CLKLO x (2 ^ PRESCALE) > SCL_LATENCY.
        // By SCL_LATENCY expansion,
        //
        //  CLKLO x (2 ^ PRESCALE) > (2 + FILTSCL + SCL_RISETIME) / (2 ^ PRESCALE)
        //
        // We use 0 for FILTSCL and assume a rise time less than 1 cycle (so, 0).
        // The inequality becomes
        //
        //  CLKLO > 2 / (2 ^ (2 * PRESCALE))
        //  CLKLO > 2 if PRESCALE = 0 (Prescaler1)
        //  CLKLO > 0 if PRESCALE > 0 (Prescaler2, Prescaler4, ...)
        //
        // So we're covered by the CLKLO >= 0x03 restriction.

        // Wait at least CLKHI cycles for (repeat) start / stop.
        let mut sethold = clkhi;
        if sethold < 0x02 {
            sethold = 0x02;
        }

        // Assume data valid after CLHI is high for half its cycles.
        let mut datavd = clkhi / 2;
        if datavd < 0x01 {
            datavd = 0x01;
        }

        Self::new(
            ClockConfiguration {
                clkhi,
                clklo,
                sethold,
                datavd,
                filtsda: 0,
                filtscl: 0,
            },
            prescaler,
        )
    }

    /// Computes timing parameters assuming an ideal circuit.
    ///
    ///
    /// Define LPI2C timings by the clock configuration values, and a prescaler.
    pub const fn new(clock_configuration: ClockConfiguration, prescaler: Prescaler) -> Self {
        const fn max(left: u32, right: u32) -> u32 {
            if left > right {
                left
            } else {
                right
            }
        }
        let busidle = max(
            (clock_configuration.clklo as u32 + clock_configuration.sethold as u32 + 2) * 2,
            clock_configuration.clkhi as u32 + 1,
        );
        Self {
            clock_configuration,
            prescaler,
            busidle,
        }
    }
    /// Returns the clock configuration.
    pub const fn clock_configuration(&self) -> ClockConfiguration {
        self.clock_configuration
    }
    /// Returns the prescaler.
    pub const fn prescaler(&self) -> Prescaler {
        self.prescaler
    }
    /// Override the BUSIDLE parameter.
    ///
    /// The minimum BUSIDLE is computed by CLKLO, SETHOLD, and CLKHI. Use
    /// this method to override the value.
    pub const fn override_busidle(mut self, busidle: u32) -> Self {
        self.busidle = busidle;
        self
    }
}

#[cfg(test)]
mod tests {
    use super::{ClockSpeed, Prescaler, Timing};

    #[test]
    fn timing_ideal() {
        let timings = Timing::ideal(8_000_000, ClockSpeed::KHz100);
        assert_eq!(timings.prescaler, Prescaler::Prescaler1);
        assert_eq!(timings.clock_configuration.clkhi, 25);
        assert_eq!(timings.clock_configuration.clklo, 50);
        assert_eq!(timings.clock_configuration.datavd, 12);
        assert_eq!(timings.clock_configuration.sethold, 25);
        assert_eq!(timings.clock_configuration.filtscl, 0);
        assert_eq!(timings.clock_configuration.filtsda, 0);

        let timings = Timing::ideal(8_000_000, ClockSpeed::KHz400);
        assert_eq!(timings.prescaler, Prescaler::Prescaler1);
        assert_eq!(timings.clock_configuration.clkhi, 5);
        assert_eq!(timings.clock_configuration.clklo, 10);
        assert_eq!(timings.clock_configuration.datavd, 2);
        assert_eq!(timings.clock_configuration.sethold, 5);
        assert_eq!(timings.clock_configuration.filtscl, 0);
        assert_eq!(timings.clock_configuration.filtsda, 0);

        let timings = Timing::ideal(8_000_000, ClockSpeed::MHz1);
        assert_eq!(timings.prescaler, Prescaler::Prescaler1);
        assert_eq!(timings.clock_configuration.clkhi, 1);
        assert_eq!(timings.clock_configuration.clklo, 3);
        assert_eq!(timings.clock_configuration.datavd, 1);
        assert_eq!(timings.clock_configuration.sethold, 2);
        assert_eq!(timings.clock_configuration.filtscl, 0);
        assert_eq!(timings.clock_configuration.filtsda, 0);
    }
}