use core::cell::Cell;
use kernel::hil;
use kernel::hil::i2c::{self, Error, I2CHwMasterClient, I2CMaster};
use kernel::platform::chip::ClockInterface;
use kernel::utilities::cells::{OptionalCell, TakeCell};
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
use kernel::utilities::registers::{register_bitfields, ReadWrite};
use kernel::utilities::StaticRef;
use crate::rcc;
pub enum I2CSpeed {
Speed100k,
Speed400k,
Speed1M,
}
#[repr(C)]
struct I2CRegisters {
cr1: ReadWrite<u32, CR1::Register>,
cr2: ReadWrite<u32, CR2::Register>,
oar1: ReadWrite<u32, OAR1::Register>,
oar2: ReadWrite<u32, OAR2::Register>,
timingr: ReadWrite<u32, TIMINGR::Register>,
timeout: ReadWrite<u32, TIMEOUT::Register>,
isr: ReadWrite<u32, ISR::Register>,
icr: ReadWrite<u32, ICR::Register>,
pecr: ReadWrite<u32, PECR::Register>,
rxdr: ReadWrite<u32, RXDR::Register>,
txdr: ReadWrite<u32, TXDR::Register>,
}
register_bitfields![u32,
CR1 [
PCEN OFFSET(23) NUMBITS(1) [],
ALERTEN OFFSET(22) NUMBITS(1) [],
SMBDEN OFFSET(21) NUMBITS(1) [],
SMBHEN OFFSET(20) NUMBITS(1) [],
GCEN OFFSET(19) NUMBITS(1) [],
WUPEN OFFSET(18) NUMBITS(1) [],
NOSTRETCH OFFSET(17) NUMBITS(1) [],
SBC OFFSET(16) NUMBITS(1) [],
RXDMAEN OFFSET(15) NUMBITS(1) [],
TXDMAEN OFFSET(14) NUMBITS(1) [],
ANOFF OFFSET(12) NUMBITS(1) [],
DNF OFFSET(8) NUMBITS(4) [],
ERRIE OFFSET(7) NUMBITS(1) [],
TCIE OFFSET(6) NUMBITS(1) [],
STOPIE OFFSET(5) NUMBITS(1) [],
NACKIE OFFSET(4) NUMBITS(1) [],
ADDRIE OFFSET(3) NUMBITS(3) [],
RXIE OFFSET(2) NUMBITS(1) [],
TXIE OFFSET(1) NUMBITS(1) [],
PE OFFSET(0) NUMBITS(1) []
],
CR2 [
PECBYTE OFFSET(26) NUMBITS(1) [],
AUTOEND OFFSET(25) NUMBITS(1) [],
RELOAD OFFSET(24) NUMBITS(1) [],
NBYTES OFFSET(16) NUMBITS(8) [],
NACK OFFSET(15) NUMBITS(1) [],
STOP OFFSET(14) NUMBITS(1) [],
START OFFSET(13) NUMBITS(1) [],
HEAD10R OFFSET(12) NUMBITS(1) [],
ADD10 OFFSET(11) NUMBITS(1) [],
RD_WRN OFFSET(10) NUMBITS(1) [],
SADD8_9 OFFSET(8) NUMBITS(2) [],
SADD7_1 OFFSET(1) NUMBITS(7) [],
SADD OFFSET(0) NUMBITS(1) []
],
OAR1 [
OA1EN OFFSET(15) NUMBITS(1) [],
OA1MODE OFFSET(10) NUMBITS(1) [],
OA1 OFFSET(0) NUMBITS(10) []
],
OAR2 [
OA2EN OFFSET(15) NUMBITS(1) [],
OA2MSK OFFSET(8) NUMBITS(3) [],
OA2 OFFSET(1) NUMBITS(7) []
],
TIMINGR [
PRESC OFFSET(28) NUMBITS(4) [],
SCLDEL OFFSET(20) NUMBITS(4) [],
SDAEL OFFSET(16) NUMBITS(4) [],
SCLH OFFSET(8) NUMBITS(8) [],
SCLL OFFSET(0) NUMBITS(8) []
],
TIMEOUT [
TEXTEN OFFSET(31) NUMBITS(1) [],
TIMEOUTB OFFSET(16) NUMBITS(12) [],
TIMOUTEN OFFSET(15) NUMBITS(1) [],
TIDLE OFFSET(12) NUMBITS(1) [],
TIMEOUTA OFFSET(0) NUMBITS(12) []
],
ISR [
ADDCODE OFFSET(17) NUMBITS(7) [],
DIR OFFSET(16) NUMBITS(1) [],
BUSY OFFSET(15) NUMBITS(1) [],
ALERT OFFSET(13) NUMBITS(1) [],
TIMEOUT OFFSET(12) NUMBITS(1) [],
PECERR OFFSET(11) NUMBITS(1) [],
OVR OFFSET(10) NUMBITS(1) [],
ARLO OFFSET(9) NUMBITS(1) [],
BERR OFFSET(8) NUMBITS(1) [],
TCR OFFSET(7) NUMBITS(1) [],
TC OFFSET(6) NUMBITS(1) [],
STOPF OFFSET(5) NUMBITS(1) [],
NACKF OFFSET(4) NUMBITS(1) [],
ADDR OFFSET(3) NUMBITS(1) [],
RXNE OFFSET(2) NUMBITS(1) [],
TXIS OFFSET(1) NUMBITS(1) [],
TXE OFFSET(0) NUMBITS(1) []
],
ICR [
ALERTCF OFFSET(13) NUMBITS(1) [],
TIMOUTCF OFFSET(12) NUMBITS(1) [],
PECCF OFFSET(11) NUMBITS(1) [],
OVRCF OFFSET(10) NUMBITS(1) [],
ARLOCF OFFSET(9) NUMBITS(1) [],
BERRCF OFFSET(8) NUMBITS(1) [],
STOPCF OFFSET(5) NUMBITS(1) [],
NACKCF OFFSET(4) NUMBITS(1) [],
ADDRCF OFFSET(3) NUMBITS(1) []
],
PECR [
PEC OFFSET(0) NUMBITS(8) []
],
RXDR [
RXDATA OFFSET(0) NUMBITS(8) []
],
TXDR [
TXDATA OFFSET(0) NUMBITS(8) []
]
];
const I2C1_BASE: StaticRef<I2CRegisters> =
unsafe { StaticRef::new(0x4000_5400 as *const I2CRegisters) };
pub struct I2C<'a> {
registers: StaticRef<I2CRegisters>,
clock: I2CClock<'a>,
master_client: OptionalCell<&'a dyn hil::i2c::I2CHwMasterClient>,
buffer: TakeCell<'static, [u8]>,
tx_position: Cell<usize>,
rx_position: Cell<usize>,
tx_len: Cell<usize>,
rx_len: Cell<usize>,
slave_address: Cell<u8>,
status: Cell<I2CStatus>,
}
#[derive(Copy, Clone, PartialEq)]
enum I2CStatus {
Idle,
Writing,
WritingReading,
Reading,
}
impl<'a> I2C<'a> {
fn new(base_addr: StaticRef<I2CRegisters>, clock: I2CClock<'a>) -> Self {
Self {
registers: base_addr,
clock,
master_client: OptionalCell::empty(),
slave_address: Cell::new(0),
buffer: TakeCell::empty(),
tx_position: Cell::new(0),
rx_position: Cell::new(0),
tx_len: Cell::new(0),
rx_len: Cell::new(0),
status: Cell::new(I2CStatus::Idle),
}
}
pub fn new_i2c1(rcc: &'a rcc::Rcc) -> Self {
Self::new(
I2C1_BASE,
I2CClock(rcc::PeripheralClock::new(
rcc::PeripheralClockType::APB1(rcc::PCLK1::I2C1),
rcc,
)),
)
}
pub fn set_speed(&self, speed: I2CSpeed, system_clock_in_mhz: usize) {
self.disable();
match speed {
I2CSpeed::Speed100k => {
let prescaler = system_clock_in_mhz / 4 - 1;
self.registers.timingr.modify(
TIMINGR::PRESC.val(prescaler as u32)
+ TIMINGR::SCLL.val(19)
+ TIMINGR::SCLH.val(15)
+ TIMINGR::SDAEL.val(2)
+ TIMINGR::SCLDEL.val(4),
);
}
I2CSpeed::Speed400k => {
let prescaler = system_clock_in_mhz / 8 - 1;
self.registers.timingr.modify(
TIMINGR::PRESC.val(prescaler as u32)
+ TIMINGR::SCLL.val(9)
+ TIMINGR::SCLH.val(3)
+ TIMINGR::SDAEL.val(3)
+ TIMINGR::SCLDEL.val(3),
);
}
I2CSpeed::Speed1M => {
panic!("i2c speed 1MHz not implemented");
}
}
self.enable();
}
pub fn is_enabled_clock(&self) -> bool {
self.clock.is_enabled()
}
pub fn enable_clock(&self) {
self.clock.enable();
}
pub fn disable_clock(&self) {
self.clock.disable();
}
pub fn handle_event(&self) {
if self.registers.isr.is_set(ISR::TXIS) {
if self.buffer.is_some() && self.tx_position.get() < self.tx_len.get() {
self.buffer.map(|buf| {
let byte = buf[self.tx_position.get()];
self.registers.txdr.write(TXDR::TXDATA.val(byte as u32));
self.tx_position.set(self.tx_position.get() + 1);
});
} else {
panic!("i2c attempted to read more bytes than the available buffer");
}
}
while self.registers.isr.is_set(ISR::RXNE) {
let byte = self.registers.rxdr.read(RXDR::RXDATA) as u8;
if self.buffer.is_some() && self.rx_position.get() < self.rx_len.get() {
self.buffer.map(|buf| {
buf[self.rx_position.get()] = byte;
self.rx_position.set(self.rx_position.get() + 1);
});
}
}
if self.registers.isr.is_set(ISR::TC) {
match self.status.get() {
I2CStatus::Writing | I2CStatus::WritingReading => {
if self.tx_position.get() < self.tx_len.get() {
self.registers.cr2.modify(CR2::STOP::SET);
self.stop();
self.master_client.map(|client| {
self.buffer
.take()
.map(|buf| client.command_complete(buf, Err(Error::DataNak)))
});
} else {
if self.status.get() == I2CStatus::Writing {
self.registers.cr2.modify(CR2::STOP::SET);
self.stop();
self.master_client.map(|client| {
self.buffer
.take()
.map(|buf| client.command_complete(buf, Ok(())))
});
} else {
self.status.set(I2CStatus::Reading);
self.start_read();
}
}
}
I2CStatus::Reading => {
let status = if self.rx_position.get() == self.rx_len.get() {
Ok(())
} else {
Err(Error::DataNak)
};
self.registers.cr2.modify(CR2::STOP::SET);
self.stop();
self.master_client.map(|client| {
self.buffer
.take()
.map(|buf| client.command_complete(buf, status))
});
}
_ => panic!("i2c status error"),
}
}
if self.registers.isr.is_set(ISR::NACKF) {
self.registers.cr2.modify(CR2::STOP::SET);
self.stop();
self.registers.icr.modify(ICR::NACKCF::SET);
self.master_client.map(|client| {
self.buffer
.take()
.map(|buf| client.command_complete(buf, Err(Error::AddressNak)))
});
}
}
pub fn handle_error(&self) {
self.master_client.map(|client| {
self.buffer
.take()
.map(|buf| client.command_complete(buf, Err(Error::DataNak)))
});
self.stop();
}
fn reset(&self) {
self.disable();
self.enable();
}
fn start_write(&self) {
self.tx_position.set(0);
self.registers
.cr2
.modify(CR2::NBYTES.val(self.tx_len.get() as u32));
self.registers
.cr2
.modify(CR2::SADD7_1.val(self.slave_address.get() as u32));
self.registers.cr2.modify(CR2::RD_WRN::CLEAR);
self.registers
.cr1
.modify(CR1::TXIE::SET + CR1::ERRIE::SET + CR1::NACKIE::SET + CR1::TCIE::SET);
self.registers.cr2.modify(CR2::START::SET);
}
fn stop(&self) {
self.registers.cr1.modify(
CR1::TXIE::CLEAR
+ CR1::ERRIE::CLEAR
+ CR1::NACKIE::CLEAR
+ CR1::TCIE::CLEAR
+ CR1::STOPIE::CLEAR
+ CR1::RXIE::CLEAR,
);
self.status.set(I2CStatus::Idle);
}
fn start_read(&self) {
self.rx_position.set(0);
self.registers
.cr2
.modify(CR2::NBYTES.val(self.rx_len.get() as u32));
self.registers
.cr2
.modify(CR2::SADD7_1.val(self.slave_address.get() as u32));
self.registers.cr2.modify(CR2::AUTOEND::CLEAR);
self.registers.cr2.modify(CR2::RD_WRN::SET);
self.registers
.cr1
.modify(CR1::ERRIE::SET + CR1::NACKIE::SET + CR1::TCIE::SET + CR1::RXIE::SET);
self.registers.cr2.modify(CR2::START::SET);
}
}
impl<'a> i2c::I2CMaster<'a> for I2C<'a> {
fn set_master_client(&self, master_client: &'a dyn I2CHwMasterClient) {
self.master_client.replace(master_client);
}
fn enable(&self) {
self.registers.cr1.modify(CR1::PE::SET);
}
fn disable(&self) {
self.registers.cr1.modify(CR1::PE::CLEAR);
}
fn write_read(
&self,
addr: u8,
data: &'static mut [u8],
write_len: usize,
read_len: usize,
) -> Result<(), (Error, &'static mut [u8])> {
if self.status.get() == I2CStatus::Idle {
self.reset();
self.status.set(I2CStatus::WritingReading);
self.slave_address.set(addr);
self.buffer.replace(data);
self.tx_len.set(write_len);
self.rx_len.set(read_len);
self.registers.cr2.modify(CR2::AUTOEND::CLEAR);
self.start_write();
Ok(())
} else {
Err((Error::Busy, data))
}
}
fn write(
&self,
addr: u8,
data: &'static mut [u8],
len: usize,
) -> Result<(), (Error, &'static mut [u8])> {
if self.status.get() == I2CStatus::Idle {
self.reset();
self.status.set(I2CStatus::Writing);
self.slave_address.set(addr);
self.buffer.replace(data);
self.tx_len.set(len);
self.registers.cr2.modify(CR2::AUTOEND::CLEAR);
self.start_write();
Ok(())
} else {
Err((Error::Busy, data))
}
}
fn read(
&self,
addr: u8,
buffer: &'static mut [u8],
len: usize,
) -> Result<(), (Error, &'static mut [u8])> {
if self.status.get() == I2CStatus::Idle {
self.reset();
self.status.set(I2CStatus::Reading);
self.slave_address.set(addr);
self.buffer.replace(buffer);
self.rx_len.set(len);
self.registers.cr2.modify(CR2::AUTOEND::CLEAR);
self.start_read();
Ok(())
} else {
Err((Error::Busy, buffer))
}
}
}
struct I2CClock<'a>(rcc::PeripheralClock<'a>);
impl ClockInterface for I2CClock<'_> {
fn is_enabled(&self) -> bool {
self.0.is_enabled()
}
fn enable(&self) {
self.0.enable();
}
fn disable(&self) {
self.0.disable();
}
}