Skip to content

Commit

Permalink
Merge pull request tock#3964 from alistair23/alistair/apollo3-spi
Browse files Browse the repository at this point in the history
chips: apollo3: iom: Support SPI operations larger then 32 bytes
  • Loading branch information
ppannuto authored May 17, 2024
2 parents cd2e13d + 4b70685 commit 03a833d
Showing 1 changed file with 151 additions and 39 deletions.
190 changes: 151 additions & 39 deletions chips/apollo3/src/iom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,8 +527,6 @@ impl<'a> Iom<'_> {
// Ensure interrupts remain enabled
regs.inten.set(0xFFFF_FFFF);

while regs.status.read(STATUS::IDLESET) != 1 {}

if irqs.is_set(INT::NAK) {
if self.op.get() == Operation::I2C {
// Disable interrupts
Expand Down Expand Up @@ -580,10 +578,20 @@ impl<'a> Iom<'_> {
}

if self.op.get() == Operation::SPI {
// Read the incoming data
if let Some(buf) = self.spi_read_buffer.take() {
while self.registers.fifoptr.read(FIFOPTR::FIFO1SIZ) > 0 {
let d = self.registers.fifopop.get().to_ne_bytes();
// The IOM doesn't correctly pop the data if we read it too fast
// there are a few erratas against the IOM when reading data
// from the FIFO (compared to DMA). Adding a small delay here is
// enough to ensure the fifoptr values update before the next
// iteration.
// See: https://ambiq.com/wp-content/uploads/2022/01/Apollo3-Blue-Errata-List.pdf
for _i in 0..3000 {
cortexm4::support::nop();
}

let d = self.registers.fifopop.get().to_ne_bytes();
let data_idx = self.read_index.get();

if let Some(b) = buf.get_mut(data_idx + 0) {
Expand All @@ -605,13 +613,85 @@ impl<'a> Iom<'_> {
}

self.spi_read_buffer.replace(buf);

if self.read_len.get() > self.read_index.get() {
let remaining_bytes = (self.read_len.get() - self.read_index.get()).min(32);
self.registers
.fifothr
.modify(FIFOTHR::FIFORTHR.val(remaining_bytes as u32));
} else {
self.registers.fifothr.modify(FIFOTHR::FIFORTHR.val(0));
}
} else {
while self.registers.fifoptr.read(FIFOPTR::FIFO1SIZ) > 0 {
// The IOM doesn't correctly pop the data if we read it too fast
// there are a few erratas against the IOM when reading data
// from the FIFO (compared to DMA). Adding a small delay here is
// enough to ensure the fifoptr values update before the next
// iteration.
// See: https://ambiq.com/wp-content/uploads/2022/01/Apollo3-Blue-Errata-List.pdf
for _i in 0..3000 {
cortexm4::support::nop();
}

let _d = self.registers.fifopop.get().to_ne_bytes();
}

self.registers.fifothr.modify(FIFOTHR::FIFORTHR.val(0));
}

// Write more data out
if self.write_index.get() < self.write_len.get() {
if let Some(write_buffer) = self.buffer.take() {
let mut transfered_bytes = 0;

// While there is some free space in FIFO0 (writing to the SPI bus) and
// at least 4 bytes free in FIFO1 (reading from the SPI bus to the
// hardware FIFO) we write up to 24 bytes of data.
//
// The `> 4` really could be `>= 4` but > gives us a little wiggle room
// as the hardware does seem a little slow at updating the FIFO size
// registers.
//
// The 24 byte limit is along the same lines, of just making sure we
// don't write too much data. I don't have a good answer of why it should
// be 24, but that seems to work reliably from testing.
//
// There isn't a specific errata for this issue, but the official HAL
// uses DMA so there aren't a lot of FIFO users for large transfers like
// this.
while self.registers.fifoptr.read(FIFOPTR::FIFO0REM) > 0
&& self.registers.fifoptr.read(FIFOPTR::FIFO1REM) > 4
&& self.write_index.get() < self.write_len.get()
&& transfered_bytes < 24
{
let idx = self.write_index.get();
let data = u32::from_le_bytes(
write_buffer[idx..(idx + 4)].try_into().unwrap_or([0; 4]),
);

self.registers.fifopush.set(data);
self.write_index.set(idx + 4);
transfered_bytes += 4;
}

self.buffer.replace(write_buffer);
}

let remaining_bytes = (self.write_len.get() - self.write_index.get()).min(32);
self.registers
.fifothr
.modify(FIFOTHR::FIFOWTHR.val(remaining_bytes as u32));
} else {
self.registers.fifothr.modify(FIFOTHR::FIFOWTHR.val(0));
}

if self.write_len.get() > 0 && self.write_index.get() >= self.write_len.get() {
if (self.write_len.get() > 0
&& self.write_index.get() >= self.write_len.get()
&& self.read_len.get() > 0
&& self.read_index.get() >= self.read_len.get())
|| irqs.is_set(INT::CMDCMP)
{
// Disable interrupts
regs.inten.set(0x00);

Expand All @@ -626,9 +706,9 @@ impl<'a> Iom<'_> {
client.read_write_done(buffer, read_buffer, self.write_len.get(), Ok(()));
});
});

return;
}

return;
}

if irqs.is_set(INT::CMDCMP) || irqs.is_set(INT::THR) {
Expand Down Expand Up @@ -1138,24 +1218,16 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
read_buffer: Option<&'static mut [u8]>,
len: usize,
) -> Result<(), (ErrorCode, &'static mut [u8], Option<&'static mut [u8]>)> {
let addr = write_buffer[0];
let write_len = write_buffer.len().min(len);
let read_len = if let Some(ref buffer) = read_buffer {
buffer.len().min(len)
} else {
0
};

let burst_len = write_len.min(32);

// Disable DMA as we don't support it
self.registers.dmacfg.write(DMACFG::DMAEN::CLEAR);

// Set the address
self.registers
.devcfg
.write(DEVCFG::DEVADDR.val(addr as u32));

// Set the DCX
self.registers.dcx.set(0);

Expand All @@ -1170,44 +1242,71 @@ impl<'a> SpiMaster<'a> for Iom<'a> {

// Start the transfer
self.registers.cmd.write(
CMD::TSIZE.val(burst_len as u32)
CMD::TSIZE.val(write_len as u32)
+ CMD::CMDSEL.val(1)
+ CMD::CONT::CLEAR
+ CMD::CMD::WRITE
+ CMD::OFFSETCNT.val(0_u32)
+ CMD::OFFSETLO.val(0),
);

while self.registers.fifoptr.read(FIFOPTR::FIFO0REM) > 4
&& self.registers.fifoptr.read(FIFOPTR::FIFO1SIZ) < 32
&& self.write_index.get() < burst_len
if let Some(buf) = read_buffer {
self.spi_read_buffer.replace(buf);
}

while self.registers.cmdstat.read(CMDSTAT::CMDSTAT) == 0x02 {}

let mut transfered_bytes = 0;

// While there is some free space in FIFO0 (writing to the SPI bus) and
// at least 4 bytes free in FIFO1 (reading from the SPI bus to the
// hardware FIFO) we write up to 24 bytes of data.
//
// The `> 4` really could be `>= 4` but > gives us a little wiggle room
// as the hardware does seem a little slow at updating the FIFO size
// registers.
//
// The 24 byte limit is along the same lines, of just making sure we
// don't write too much data. I don't have a good answer of why it should
// be 24, but that seems to work reliably from testing.
//
// There isn't a specific errata for this issue, but the official HAL
// uses DMA so there aren't a lot of FIFO users for large transfers like
// this.
while self.registers.fifoptr.read(FIFOPTR::FIFO0REM) > 0
&& self.registers.fifoptr.read(FIFOPTR::FIFO1REM) > 4
&& self.write_index.get() < write_len
&& transfered_bytes < 24
{
let idx = self.write_index.get();
let data =
u32::from_le_bytes(write_buffer[idx..(idx + 4)].try_into().unwrap_or([0; 4]));

self.registers.fifopush.set(data);
self.write_index.set(idx + 4);
}
transfered_bytes += 4;

if let Some(buf) = read_buffer {
while self.registers.fifoptr.read(FIFOPTR::FIFO1SIZ) > 0 {
let d = self.registers.fifopop.get().to_ne_bytes();
if let Some(buf) = self.spi_read_buffer.take() {
if self.registers.fifoptr.read(FIFOPTR::FIFO1SIZ) > 0
&& self.read_index.get() < read_len
{
let d = self.registers.fifopop.get().to_ne_bytes();

let data_idx = self.read_index.get();
let data_idx = self.read_index.get();

buf[data_idx + 0] = d[0];
buf[data_idx + 1] = d[1];
buf[data_idx + 2] = d[2];
buf[data_idx + 3] = d[3];
buf[data_idx + 0] = d[0];
buf[data_idx + 1] = d[1];
buf[data_idx + 2] = d[2];
buf[data_idx + 3] = d[3];

self.read_index.set(data_idx + 4);
}
self.read_index.set(data_idx + 4);
}

self.spi_read_buffer.replace(buf);
} else {
while self.registers.fifoptr.read(FIFOPTR::FIFO1SIZ) > 0 {
let _d = self.registers.fifopop.get().to_ne_bytes();
self.spi_read_buffer.replace(buf);
} else {
if self.registers.fifoptr.read(FIFOPTR::FIFO1SIZ) > 0 {
let _d = self.registers.fifopop.get();
}
}
}

Expand All @@ -1217,6 +1316,25 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
self.read_len.set(read_len);
self.op.set(Operation::SPI);

if read_len > self.read_index.get() {
let remaining_bytes = (read_len - self.read_index.get()).min(32);
self.registers
.fifothr
.modify(FIFOTHR::FIFORTHR.val(remaining_bytes as u32));
} else {
self.registers.fifothr.modify(FIFOTHR::FIFORTHR.val(0));
}

if write_len > self.write_index.get() {
let remaining_bytes = (self.write_len.get() - self.write_index.get()).min(32);

self.registers
.fifothr
.modify(FIFOTHR::FIFOWTHR.val(remaining_bytes as u32));
} else {
self.registers.fifothr.modify(FIFOTHR::FIFOWTHR.val(0));
}

// Enable interrupts
self.registers.inten.set(0xFFFF_FFFF);

Expand All @@ -1229,8 +1347,6 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
// Disable DMA as we don't support it
self.registers.dmacfg.write(DMACFG::DMAEN::CLEAR);

// We don't set an address, as we don't have one

// Set the DCX
self.registers.dcx.set(0);

Expand Down Expand Up @@ -1263,8 +1379,6 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
// Disable DMA as we don't support it
self.registers.dmacfg.write(DMACFG::DMAEN::CLEAR);

// We don't set an address, as we don't have one

// Set the DCX
self.registers.dcx.set(0);

Expand Down Expand Up @@ -1302,8 +1416,6 @@ impl<'a> SpiMaster<'a> for Iom<'a> {
// Disable DMA as we don't support it
self.registers.dmacfg.write(DMACFG::DMAEN::CLEAR);

// We don't set an address, as we don't have one

// Set the DCX
self.registers.dcx.set(0);

Expand Down

0 comments on commit 03a833d

Please sign in to comment.