rust: Add i2c module

Just enough to support NS2009 for now.
This commit is contained in:
Wladimir J. van der Laan 2019-05-13 11:43:06 +00:00
parent 3ba2204102
commit 0572038b07
3 changed files with 92 additions and 1 deletions

View File

@ -6,5 +6,5 @@ members = [
[patch.crates-io]
riscv-rt = { git = "https://github.com/rust-embedded/riscv-rt.git", rev = "ecc1344ffc9af1c88b3dd76b83ad56284672f888"}
k210-pac = { git = "https://github.com/riscv-rust/k210-pac.git", rev = "c24d654f5ad39eecf9758b852d0f54a88cefad3f"}
k210-pac = { git = "https://github.com/riscv-rust/k210-pac.git", rev = "18f8bdca27d3befa0a92cea2a784f3f5bfc80ae7"}
k210-hal = { git = "https://github.com/riscv-rust/k210-hal.git", rev = "725418b922d9bb6d8593ba4087b5d90f49e1b8e7" }

View File

@ -1,6 +1,7 @@
pub mod fpioa;
pub mod gpio;
pub mod gpiohs;
pub mod i2c;
pub mod sleep;
pub mod spi;
pub mod sysctl;

View File

@ -0,0 +1,90 @@
use core::convert::TryInto;
use core::result::Result;
use k210_hal::pac;
use crate::soc::sysctl;
// TODO parametrize type, other I2C than I2C0
fn clk_init()
{
sysctl::clock_enable(sysctl::clock::I2C0);
sysctl::clock_set_threshold(sysctl::threshold::I2C0, 3);
}
pub fn init(slave_address: u16, address_width: u32, i2c_clk: u32)
{
assert!(address_width == 7 || address_width == 10);
clk_init();
let v_i2c_freq = sysctl::clock_get_freq(sysctl::clock::I2C0);
let v_period_clk_cnt = v_i2c_freq / i2c_clk / 2;
let v_period_clk_cnt: u16 = v_period_clk_cnt.try_into().unwrap();
let v_period_clk_cnt = if v_period_clk_cnt == 0 { 1 } else { v_period_clk_cnt };
unsafe {
let ptr = pac::I2C0::ptr();
(*ptr).enable.write(|w| w.bits(0));
(*ptr).con.write(|w| w.master_mode().bit(true)
.slave_disable().bit(true)
.restart_en().bit(true)
.addr_slave_width().bit(address_width == 10) // TODO variant
.speed().bits(1)); // TODO variant
(*ptr).ss_scl_hcnt.write(|w| w.count().bits(v_period_clk_cnt));
(*ptr).ss_scl_lcnt.write(|w| w.count().bits(v_period_clk_cnt));
(*ptr).tar.write(|w| w.address().bits(slave_address));
(*ptr).intr_mask.write(|w| w.bits(0));
(*ptr).dma_cr.write(|w| w.bits(0x3));
(*ptr).dma_rdlr.write(|w| w.bits(0));
(*ptr).dma_tdlr.write(|w| w.bits(4));
(*ptr).enable.write(|w| w.enable().bit(true));
}
}
pub fn recv_data(send_buf: &[u8], receive_buf: &mut [u8]) -> Result<(), ()>
{
unsafe {
let ptr = pac::I2C0::ptr();
let mut txi = 0;
let mut tx_left = send_buf.len();
while tx_left != 0 {
let fifo_len = 8 - ((*ptr).txflr.read().bits() as usize);
let fifo_len = if tx_left < fifo_len { tx_left } else { fifo_len };
for _ in 0..fifo_len {
(*ptr).data_cmd.write(|w| w.data().bits(send_buf[txi]));
txi += 1;
}
if (*ptr).tx_abrt_source.read().bits() != 0 {
return Err(());
}
tx_left -= fifo_len;
}
let mut cmd_count = receive_buf.len();
let mut rx_left = receive_buf.len();
let mut rxi = 0;
while cmd_count != 0 || rx_left != 0 {
/* XXX this is a kind of strange construction, sanity check */
let fifo_len = (*ptr).rxflr.read().bits() as usize;
let fifo_len = if rx_left < fifo_len { rx_left } else { fifo_len };
for _ in 0..fifo_len {
receive_buf[rxi] = (*ptr).data_cmd.read().data().bits();
rxi += 1;
}
rx_left -= fifo_len;
/* send 0x100 for every byte that we want to receive */
let fifo_len = 8 - (*ptr).txflr.read().bits() as usize;
let fifo_len = if cmd_count < fifo_len { cmd_count } else { fifo_len };
for _ in 0..fifo_len {
(*ptr).data_cmd.write(|w| w.cmd().bit(true));
}
if (*ptr).tx_abrt_source.read().bits() != 0 {
return Err(());
}
cmd_count -= fifo_len;
}
}
Ok(())
}