Skip to content

Commit 402580a

Browse files
committed
Cache LPSPI CCR[DBT] and CCR[SCKDIV] in memory
On the RT1180, these two fields are WO/RAZ. Since we can't trust the hardware to retrieve these values for us, we cache them in memory. Writes will behave as before. The driver ensures that all accesses on CCR pass through the cache.
1 parent dc81c0a commit 402580a

File tree

1 file changed

+64
-20
lines changed

1 file changed

+64
-20
lines changed

src/common/lpspi.rs

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ impl Transaction {
359359
/// Sets the clock speed parameters.
360360
///
361361
/// This should only happen when the LPSPI peripheral is disabled.
362-
fn set_spi_clock(source_clock_hz: u32, spi_clock_hz: u32, reg: &ral::lpspi::RegisterBlock) {
362+
fn compute_spi_clock(source_clock_hz: u32, spi_clock_hz: u32) -> ClockConfigs {
363363
// Round up, so we always get a resulting SPI clock that is
364364
// equal or less than the requested frequency.
365365
let half_div =
@@ -372,16 +372,16 @@ fn set_spi_clock(source_clock_hz: u32, spi_clock_hz: u32, reg: &ral::lpspi::Regi
372372
// Because half_div is in range [3,128], sckdiv is in range [4, 254].
373373
let sckdiv = 2 * (half_div - 1);
374374

375-
ral::write_reg!(ral::lpspi, reg, CCR,
375+
ClockConfigs {
376376
// Delay between two clock transitions of two consecutive transfers
377377
// is exactly sckdiv/2, which causes the transfer to be seamless.
378-
DBT: half_div - 1,
378+
dbt: (half_div - 1) as u8,
379379
// Add one sckdiv/2 setup and hold time before and after the transfer,
380380
// to make sure the signal is stable at sample time
381-
PCSSCK: half_div - 1,
382-
SCKPCS: half_div - 1,
383-
SCKDIV: sckdiv
384-
);
381+
pcssck: (half_div - 1) as u8,
382+
sckpcs: (half_div - 1) as u8,
383+
sckdiv: sckdiv as u8,
384+
}
385385
}
386386

387387
/// LPSPI clock configurations.
@@ -416,6 +416,47 @@ pub struct ClockConfigs {
416416
pub sckdiv: u8,
417417
}
418418

419+
impl ClockConfigs {
420+
const fn as_raw(self) -> u32 {
421+
use ral::lpspi::CCR;
422+
(self.sckpcs as u32) << CCR::SCKPCS::offset
423+
| (self.pcssck as u32) << CCR::PCSSCK::offset
424+
| (self.dbt as u32) << CCR::DBT::offset
425+
| (self.sckdiv as u32) << CCR::SCKDIV::offset
426+
}
427+
}
428+
429+
/// In-memory clock configuration register values.
430+
///
431+
/// Newer LPSPI IP blocks, like those found on the 1180, have `CCR[DBT]`
432+
/// and `CCR[SCKDIV]` fields that are WO/RAZ. RAZ is incompatible
433+
/// with older IP blocks, like those found on all the other MCUs.
434+
///
435+
/// If we cache these values, all RMW on CCR will behave as if we
436+
/// read those values through the register.
437+
struct CcrCache {
438+
dbt: u8,
439+
sckdiv: u8,
440+
}
441+
442+
impl CcrCache {
443+
/// Reac the clock configuration values, considering the cached values.
444+
fn read_ccr(&self, lpspi: &ral::lpspi::RegisterBlock) -> ClockConfigs {
445+
let (sckpcs, pcssck) = ral::read_reg!(ral::lpspi, lpspi, CCR, SCKPCS, PCSSCK);
446+
ClockConfigs {
447+
sckpcs: sckpcs as u8,
448+
pcssck: pcssck as u8,
449+
dbt: self.dbt,
450+
sckdiv: self.sckdiv,
451+
}
452+
}
453+
/// Update the cached values.
454+
fn update(&mut self, clock_configs: ClockConfigs) {
455+
self.dbt = clock_configs.dbt;
456+
self.sckdiv = clock_configs.sckdiv;
457+
}
458+
}
459+
419460
/// An LPSPI driver.
420461
///
421462
/// The driver exposes low-level methods for coordinating
@@ -432,6 +473,7 @@ pub struct Lpspi<P, const N: u8> {
432473
pins: P,
433474
bit_order: BitOrder,
434475
mode: Mode,
476+
ccr_cache: CcrCache,
435477
}
436478

437479
/// Pins for a LPSPI device.
@@ -505,6 +547,8 @@ impl<P, const N: u8> Lpspi<P, N> {
505547
pins,
506548
bit_order: BitOrder::default(),
507549
mode: MODE_0,
550+
// Once we issue a reset, below, these are zero.
551+
ccr_cache: CcrCache { dbt: 0, sckdiv: 0 },
508552
};
509553

510554
// Reset and disable
@@ -593,7 +637,7 @@ impl<P, const N: u8> Lpspi<P, N> {
593637
/// The handle to a [`Disabled`](crate::lpspi::Disabled) driver lets you modify
594638
/// LPSPI settings that require a fully disabled peripheral.
595639
pub fn disabled<R>(&mut self, func: impl FnOnce(&mut Disabled<N>) -> R) -> R {
596-
let mut disabled = Disabled::new(&mut self.lpspi);
640+
let mut disabled = Disabled::new(&mut self.lpspi, &mut self.ccr_cache);
597641
func(&mut disabled)
598642
}
599643

@@ -945,7 +989,7 @@ impl<P, const N: u8> Lpspi<P, N> {
945989
let cfgr1 = ral::read_reg!(ral::lpspi, self.lpspi, CFGR1);
946990
let dmr0 = ral::read_reg!(ral::lpspi, self.lpspi, DMR0);
947991
let dmr1 = ral::read_reg!(ral::lpspi, self.lpspi, DMR1);
948-
let ccr = ral::read_reg!(ral::lpspi, self.lpspi, CCR);
992+
let ccr = self.clock_configs().as_raw();
949993
let fcr = ral::read_reg!(ral::lpspi, self.lpspi, FCR);
950994

951995
// Backup enabled state
@@ -1002,14 +1046,7 @@ impl<P, const N: u8> Lpspi<P, N> {
10021046
/// These values are decided by calls to [`set_clock_hz`](Disabled::set_clock_hz)
10031047
/// and [`set_clock_configs`](Disabled::set_clock_configs).
10041048
pub fn clock_configs(&self) -> ClockConfigs {
1005-
let (sckpcs, pcssck, dbt, sckdiv) =
1006-
ral::read_reg!(ral::lpspi, self.lpspi, CCR, SCKPCS, PCSSCK, DBT, SCKDIV);
1007-
ClockConfigs {
1008-
sckpcs: sckpcs as u8,
1009-
pcssck: pcssck as u8,
1010-
dbt: dbt as u8,
1011-
sckdiv: sckdiv as u8,
1012-
}
1049+
self.ccr_cache.read_ccr(&self.lpspi)
10131050
}
10141051

10151052
/// Produce a transaction that considers bus-managed software state.
@@ -1175,33 +1212,40 @@ fn set_watermark(lpspi: &ral::lpspi::RegisterBlock, direction: Direction, waterm
11751212
pub struct Disabled<'a, const N: u8> {
11761213
lpspi: &'a ral::lpspi::Instance<N>,
11771214
men: bool,
1215+
ccr_cache: &'a mut CcrCache,
11781216
}
11791217

11801218
impl<'a, const N: u8> Disabled<'a, N> {
1181-
fn new(lpspi: &'a mut ral::lpspi::Instance<N>) -> Self {
1219+
fn new(lpspi: &'a mut ral::lpspi::Instance<N>, ccr_cache: &'a mut CcrCache) -> Self {
11821220
let men = ral::read_reg!(ral::lpspi, lpspi, CR, MEN == MEN_1);
11831221

11841222
// Request disable
11851223
ral::modify_reg!(ral::lpspi, lpspi, CR, MEN: MEN_0);
11861224
// Wait for the driver to finish its current transfer
11871225
// and enter disabled state
11881226
while ral::read_reg!(ral::lpspi, lpspi, CR, MEN == MEN_1) {}
1189-
Self { lpspi, men }
1227+
Self {
1228+
lpspi,
1229+
men,
1230+
ccr_cache,
1231+
}
11901232
}
11911233

11921234
/// Set the LPSPI clock speed (Hz).
11931235
///
11941236
/// `source_clock_hz` is the LPSPI peripheral clock speed. To specify the
11951237
/// peripheral clock, see the [`ccm::lpspi_clk`](crate::ccm::lpspi_clk) documentation.
11961238
pub fn set_clock_hz(&mut self, source_clock_hz: u32, clock_hz: u32) {
1197-
set_spi_clock(source_clock_hz, clock_hz, self.lpspi);
1239+
let clock_configs = compute_spi_clock(source_clock_hz, clock_hz);
1240+
self.set_clock_configs(clock_configs);
11981241
}
11991242

12001243
/// Set LPSPI timing configurations.
12011244
///
12021245
/// If you're not sure how to select these timing values, prefer
12031246
/// [`set_clock_hz`](Self::set_clock_hz).
12041247
pub fn set_clock_configs(&mut self, timing: ClockConfigs) {
1248+
self.ccr_cache.update(timing);
12051249
ral::write_reg!(ral::lpspi, self.lpspi, CCR,
12061250
SCKPCS: timing.sckpcs as u32,
12071251
PCSSCK: timing.pcssck as u32,

0 commit comments

Comments
 (0)