Skip to content

Commit

Permalink
sam4l: spi: adapt SPI implementation to leasable buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
alevy committed Sep 13, 2024
1 parent 34cedb3 commit 46d64ee
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 63 deletions.
15 changes: 7 additions & 8 deletions boards/imix/src/test/spi_dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use core::ptr::addr_of_mut;

use kernel::hil::gpio::Configure;
use kernel::hil::spi::{self, SpiMaster};
use kernel::utilities::leasable_buffer::SubSliceMut;
use kernel::ErrorCode;

#[allow(unused_variables, dead_code)]
Expand All @@ -31,17 +32,16 @@ impl spi::SpiMasterClient for DummyCB {
#[allow(unused_variables, dead_code)]
fn read_write_done(
&self,
write: &'static mut [u8],
read: Option<&'static mut [u8]>,
len: usize,
_status: Result<(), ErrorCode>,
write: SubSliceMut<'static, u8>,
read: Option<SubSliceMut<'static, u8>>,
status: Result<usize, ErrorCode>,
) {
unsafe {
// do actual stuff
// TODO verify SPI return value
let _ = self
.spi
.read_write_bytes(&mut *addr_of_mut!(A5), None, A5.len());
.read_write_bytes((&mut *addr_of_mut!(A5) as &mut [u8]).into(), None);

// FLOP = !FLOP;
// let len: usize = BUF1.len();
Expand Down Expand Up @@ -89,9 +89,8 @@ pub unsafe fn spi_dummy_test(spi: &'static sam4l::spi::SpiHw<'static>) {

let len = BUF2.len();
if spi.read_write_bytes(
&mut *addr_of_mut!(BUF2),
Some(&mut *addr_of_mut!(BUF1)),
len,
(&mut *addr_of_mut!(BUF2) as &mut [u8]).into(),
Some((&mut *addr_of_mut!(BUF1) as &mut [u8]).into()),
) != Ok(())
{
loop {
Expand Down
27 changes: 12 additions & 15 deletions boards/imix/src/test/spi_loopback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use core::ptr::addr_of_mut;
use kernel::component::Component;
use kernel::debug;
use kernel::hil::spi::{self, SpiMasterDevice};
use kernel::utilities::leasable_buffer::SubSliceMut;
use kernel::ErrorCode;

#[allow(unused_variables, dead_code)]
Expand Down Expand Up @@ -49,14 +50,13 @@ impl spi::SpiMasterClient for SpiLoopback {
#[allow(unused_variables, dead_code)]
fn read_write_done(
&self,
write: &'static mut [u8],
read: Option<&'static mut [u8]>,
len: usize,
status: Result<(), ErrorCode>,
mut write: SubSliceMut<'static, u8>,
read: Option<SubSliceMut<'static, u8>>,
status: Result<usize, ErrorCode>,
) {
let mut good = true;
let read = read.unwrap();
for (c, v) in write.iter().enumerate() {
for (c, v) in write[..].iter().enumerate() {
if read[c] != *v {
debug!(
"SPI test error at index {}: wrote {} but read {}",
Expand All @@ -75,7 +75,7 @@ impl spi::SpiMasterClient for SpiLoopback {
write[i] = counter.wrapping_add(i as u8);
}

if let Err((e, _, _)) = self.spi.read_write_bytes(write, Some(read), len) {
if let Err((e, _, _)) = self.spi.read_write_bytes(write, Some(read)) {
panic!(
"Could not continue SPI test, error on read_write_bytes is {:?}",
e
Expand All @@ -98,9 +98,8 @@ pub unsafe fn spi_loopback_test(

let len = WBUF.len();
if let Err((e, _, _)) = spi.read_write_bytes(
&mut *addr_of_mut!(WBUF),
Some(&mut *addr_of_mut!(RBUF)),
len,
(&mut *addr_of_mut!(WBUF) as &mut [u8]).into(),
Some((&mut *addr_of_mut!(RBUF) as &mut [u8]).into()),
) {
panic!(
"Could not start SPI test, error on read_write_bytes is {:?}",
Expand Down Expand Up @@ -130,9 +129,8 @@ pub unsafe fn spi_two_loopback_test(mux: &'static MuxSpiMaster<'static, sam4l::s

let len = WBUF.len();
if let Err((e, _, _)) = spi_fast.read_write_bytes(
&mut *addr_of_mut!(WBUF),
Some(&mut *addr_of_mut!(RBUF)),
len,
(&mut *addr_of_mut!(WBUF) as &mut [u8]).into(),
Some((&mut *addr_of_mut!(RBUF) as &mut [u8]).into()),
) {
panic!(
"Could not start SPI test, error on read_write_bytes is {:?}",
Expand All @@ -142,9 +140,8 @@ pub unsafe fn spi_two_loopback_test(mux: &'static MuxSpiMaster<'static, sam4l::s

let len = WBUF2.len();
if let Err((e, _, _)) = spi_slow.read_write_bytes(
&mut *addr_of_mut!(WBUF2),
Some(&mut *addr_of_mut!(RBUF2)),
len,
(&mut *addr_of_mut!(WBUF2) as &mut [u8]).into(),
Some((&mut *addr_of_mut!(RBUF2) as &mut [u8]).into()),
) {
panic!(
"Could not start SPI test, error on read_write_bytes is {:?}",
Expand Down
81 changes: 51 additions & 30 deletions chips/sam4l/src/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use kernel::hil::spi::SpiMasterClient;
use kernel::hil::spi::SpiSlaveClient;
use kernel::platform::chip::ClockInterface;
use kernel::utilities::cells::OptionalCell;
use kernel::utilities::leasable_buffer::SubSliceMut;
use kernel::utilities::peripheral_management::{PeripheralManagement, PeripheralManager};
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
use kernel::utilities::registers::{self, register_bitfields, ReadOnly, ReadWrite, WriteOnly};
Expand Down Expand Up @@ -443,35 +444,26 @@ impl<'a> SpiHw<'a> {
// the caller, and the caller may want to be able write into it.
fn read_write_bytes(
&self,
write_buffer: Option<&'static mut [u8]>,
read_buffer: Option<&'static mut [u8]>,
len: usize,
write_buffer: Option<SubSliceMut<'static, u8>>,
read_buffer: Option<SubSliceMut<'static, u8>>,
) -> Result<
(),
(
ErrorCode,
Option<&'static mut [u8]>,
Option<&'static mut [u8]>,
Option<SubSliceMut<'static, u8>>,
Option<SubSliceMut<'static, u8>>,
),
> {
if write_buffer.is_none() && read_buffer.is_none() {
return Err((ErrorCode::INVAL, write_buffer, read_buffer));
}
let count = match (&write_buffer, &read_buffer) {
(Some(ref wb), Some(ref rb)) => cmp::min(wb.len(), rb.len()),
(Some(ref wb), None) => wb.len(),
(None, Some(ref rb)) => rb.len(),
(None, None) => return Err((ErrorCode::INVAL, write_buffer, read_buffer)),
};

// Start by enabling the SPI driver.
self.enable();

// Determine how many bytes to move based on the shortest of the
// write_buffer length, the read_buffer length, and the user requested
// len.
let mut count: usize = len;
write_buffer
.as_ref()
.map(|buf| count = cmp::min(count, buf.len()));
read_buffer
.as_ref()
.map(|buf| count = cmp::min(count, buf.len()));

// Configure DMA to transfer that many bytes.
self.dma_length.set(count);

Expand All @@ -491,19 +483,19 @@ impl<'a> SpiHw<'a> {
.set(self.transfers_in_progress.get() + 1);
self.dma_read.map(move |read| {
read.enable();
read.do_transfer(DMAPeripheral::SPI_RX, rbuf, count);
read.do_transfer(DMAPeripheral::SPI_RX, rbuf.take(), count);
});
});

// The ordering of these operations matters.
// For transfers 4 bytes or longer, this will work as expected.
// For shorter transfers, the first byte will be missing.
write_buffer.map(|wbuf| {
write_buffer.map(|buf| {
self.transfers_in_progress
.set(self.transfers_in_progress.get() + 1);
self.dma_write.map(move |write| {
write.enable();
write.do_transfer(DMAPeripheral::SPI_TX, wbuf, count);
write.do_transfer(DMAPeripheral::SPI_TX, buf.take(), count);
});
});

Expand Down Expand Up @@ -571,19 +563,29 @@ impl<'a> spi::SpiMaster<'a> for SpiHw<'a> {
// the caller, and the caller may want to be able write into it.
fn read_write_bytes(
&self,
write_buffer: &'static mut [u8],
read_buffer: Option<&'static mut [u8]>,
len: usize,
) -> Result<(), (ErrorCode, &'static mut [u8], Option<&'static mut [u8]>)> {
write_buffer: SubSliceMut<'static, u8>,
read_buffer: Option<SubSliceMut<'static, u8>>,
) -> Result<
(),
(
ErrorCode,
SubSliceMut<'static, u8>,
Option<SubSliceMut<'static, u8>>,
),
> {
// If busy, don't start.
if self.is_busy() {
return Err((ErrorCode::BUSY, write_buffer, read_buffer));
}

if let Err((err, write_buffer, read_buffer)) =
self.read_write_bytes(Some(write_buffer), read_buffer, len)
self.read_write_bytes(Some(write_buffer), read_buffer)
{
Err((err, write_buffer.unwrap(), read_buffer))
Err((
err,
write_buffer.unwrap_or((&mut [] as &mut [u8]).into()),
read_buffer,
))
} else {
Ok(())
}
Expand Down Expand Up @@ -686,7 +688,26 @@ impl<'a> spi::SpiSlave<'a> for SpiHw<'a> {
Option<&'static mut [u8]>,
),
> {
self.read_write_bytes(write_buffer, read_buffer, len)
let write_buffer = write_buffer.map(|b| {
let mut buf: SubSliceMut<u8> = b.into();
if buf.len() > len {
buf.slice(..len);
}
buf
});
let read_buffer = read_buffer.map(|b| {
let mut buf: SubSliceMut<u8> = b.into();
if buf.len() > len {
buf.slice(..len);
}
buf
});

if let Err((e, mwb, mrb)) = self.read_write_bytes(write_buffer, read_buffer) {
Err((e, mwb.map(|b| b.take()), mrb.map(|b| b.take())))
} else {
Ok(())
}
}

fn set_polarity(&self, polarity: ClockPolarity) -> Result<(), ErrorCode> {
Expand Down Expand Up @@ -740,7 +761,7 @@ impl DMAClient for SpiHw<'_> {
SpiRole::SpiMaster => {
self.client.map(|cb| {
txbuf.map(|txbuf| {
cb.read_write_done(txbuf, rxbuf, len, Ok(()));
cb.read_write_done(txbuf.into(), rxbuf.map(|b| b.into()), Ok(len));
});
});
}
Expand Down
30 changes: 20 additions & 10 deletions chips/sam4l/src/usart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use kernel::hil::spi;
use kernel::hil::spi::cs::ChipSelectPolar;
use kernel::hil::uart;
use kernel::utilities::cells::OptionalCell;
use kernel::utilities::leasable_buffer::SubSliceMut;
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite, WriteOnly};
use kernel::utilities::StaticRef;
Expand Down Expand Up @@ -702,7 +703,11 @@ impl<'a> USART<'a> {
// state.
let len = self.tx_len.get();
self.tx_len.set(0);
client.read_write_done(txbuffer.unwrap(), rxbuf, len, Ok(()));
client.read_write_done(
txbuffer.unwrap().into(),
rxbuf.map(|b| b.into()),
Ok(len),
);
}
}
});
Expand Down Expand Up @@ -1086,20 +1091,25 @@ impl<'a> spi::SpiMaster<'a> for USART<'a> {

fn read_write_bytes(
&self,
write_buffer: &'static mut [u8],
read_buffer: Option<&'static mut [u8]>,
len: usize,
) -> Result<(), (ErrorCode, &'static mut [u8], Option<&'static mut [u8]>)> {
write_buffer: SubSliceMut<'static, u8>,
read_buffer: Option<SubSliceMut<'static, u8>>,
) -> Result<
(),
(
ErrorCode,
SubSliceMut<'static, u8>,
Option<SubSliceMut<'static, u8>>,
),
> {
let usart = &USARTRegManager::new(self);

self.enable_tx(usart);
self.enable_rx(usart);

// Calculate the correct length for the transmission
let buflen = read_buffer.as_ref().map_or(write_buffer.len(), |rbuf| {
let count = read_buffer.as_ref().map_or(write_buffer.len(), |rbuf| {
cmp::min(rbuf.len(), write_buffer.len())
});
let count = cmp::min(buflen, len);

self.tx_len.set(count);

Expand Down Expand Up @@ -1128,12 +1138,12 @@ impl<'a> spi::SpiMaster<'a> for USART<'a> {
self.usart_tx_state.set(USARTStateTX::DMA_Transmitting);
self.usart_rx_state.set(USARTStateRX::Idle);
dma.enable();
dma.do_transfer(self.tx_dma_peripheral, write_buffer, count);
dma.do_transfer(self.tx_dma_peripheral, write_buffer.take(), count);

// Start the read transaction.
self.usart_rx_state.set(USARTStateRX::DMA_Receiving);
read.enable();
read.do_transfer(self.rx_dma_peripheral, rbuf, count);
read.do_transfer(self.rx_dma_peripheral, rbuf.take(), count);
});
});
});
Expand All @@ -1143,7 +1153,7 @@ impl<'a> spi::SpiMaster<'a> for USART<'a> {
self.usart_tx_state.set(USARTStateTX::DMA_Transmitting);
self.usart_rx_state.set(USARTStateRX::Idle);
dma.enable();
dma.do_transfer(self.tx_dma_peripheral, write_buffer, count);
dma.do_transfer(self.tx_dma_peripheral, write_buffer.take(), count);
});
}

Expand Down

0 comments on commit 46d64ee

Please sign in to comment.