|
4 | 4 |
|
5 | 5 | #![no_std] |
6 | 6 |
|
7 | | -use cortex_m_rt::pre_init; |
8 | | - |
9 | 7 | #[cfg(feature = "h743")] |
10 | 8 | use stm32h7::stm32h743 as device; |
11 | 9 |
|
12 | 10 | #[cfg(feature = "h753")] |
13 | 11 | use stm32h7::stm32h753 as device; |
14 | 12 |
|
15 | | -#[cfg(any(feature = "h743", feature = "h753"))] |
16 | | -#[pre_init] |
17 | | -unsafe fn system_pre_init() { |
18 | | - // Configure the power supply to latch the LDO on and prevent further |
19 | | - // reconfiguration. |
20 | | - // |
21 | | - // Normally we would use Peripherals::take() to safely get a reference to |
22 | | - // the PWR block, but that function expects RAM to be initialized and |
23 | | - // writable. At this point, RAM is neither -- because the chip requires us |
24 | | - // to get the power supply configuration right _before it guarantees that |
25 | | - // RAM will work._ |
26 | | - // |
27 | | - // Another case of the cortex_m/stm32 crates being designed with simpler |
28 | | - // systems in mind. |
29 | | - |
30 | | - // Synthesize a pointer using a const fn (which won't hit RAM) and then |
31 | | - // convert it to a reference. We can have a reference to PWR because it's |
32 | | - // hardware, and is thus not uninitialized. |
33 | | - let pwr = &*device::PWR::ptr(); |
34 | | - // Poke CR3 to enable the LDO and prevent further writes. |
35 | | - pwr.cr3.modify(|_, w| w.ldoen().set_bit()); |
36 | | - |
37 | | - // Busy-wait until the ACTVOSRDY bit says that we've stabilized at VOS3. |
38 | | - while !pwr.csr1.read().actvosrdy().bit() { |
39 | | - // spin |
40 | | - } |
41 | | - |
42 | | - // Turn on the internal RAMs. |
43 | | - let rcc = &*device::RCC::ptr(); |
44 | | - rcc.ahb2enr.modify(|_, w| { |
45 | | - w.sram1en() |
46 | | - .set_bit() |
47 | | - .sram2en() |
48 | | - .set_bit() |
49 | | - .sram3en() |
50 | | - .set_bit() |
51 | | - }); |
52 | | - |
53 | | - // Okay, yay, we can use some RAMs now. |
54 | | - |
55 | | - #[cfg(any(feature = "h743", feature = "h753"))] |
56 | | - { |
57 | | - // Workaround for erratum 2.2.9 "Reading from AXI SRAM may lead to data |
58 | | - // read corruption" - limits AXI SRAM read concurrency. |
59 | | - let axi = &*device::AXI::ptr(); |
60 | | - axi.targ7_fn_mod |
61 | | - .modify(|_, w| w.read_iss_override().set_bit()); |
62 | | - } |
63 | | - |
64 | | - // We'll do the rest in system_init. |
| 13 | +// System pre-init hook for establishing system properties required by Rust. |
| 14 | +// |
| 15 | +// This routine must run before anything touches RAM! The cortex-m-rt crate's |
| 16 | +// Reset handler ensures this. As a result, we have to write this in raw |
| 17 | +// assembly code, to avoid trying to push/pop a stack frame. |
| 18 | +// |
| 19 | +// Be very careful about reordering or removing things from this function. |
| 20 | +core::arch::global_asm! { |
| 21 | + ".global __pre_init", |
| 22 | + ".type __pre_init_,%function", |
| 23 | + ".thumb_func", |
| 24 | + ".cfi_startproc", |
| 25 | + "__pre_init:", |
| 26 | + |
| 27 | + // PWR.CR3 has a write-once feature on the LDO enable bit. The processor |
| 28 | + // would like the power configuration to be stable before it guarantees that |
| 29 | + // writes to RAM will succeed (reference manual 6.4.1 "System supply |
| 30 | + // startup"). We're actually perfectly happy with the reset supply |
| 31 | + // configuration, which is VOS3 on the LDO. So, we'll write PWR.CR3 just to |
| 32 | + // lock it: |
| 33 | + " movw r0, :lower16:{PWR_addr}", |
| 34 | + " movt r0, :upper16:{PWR_addr}", |
| 35 | + " ldr r1, [r0, #{PWR_CR3_offset}]", |
| 36 | + " str r1, [r0, #{PWR_CR3_offset}]", |
| 37 | + |
| 38 | + // Technically we're supposed to ensure that we're stable at VOS3 before |
| 39 | + // continuing; this should already be ensured before our code was allowed to |
| 40 | + // run, but for safety's sake: |
| 41 | + "1: ldr r1, [r0, #{PWR_CSR1_offset}]", |
| 42 | + " tst r1, #(1 << {PWR_CSR1_ACTVOSRDY_bit})", |
| 43 | + " beq 1b", |
| 44 | + |
| 45 | + // Turn on all of the smaller non-TCM non-AXI SRAMs, in case the program |
| 46 | + // puts data there. |
| 47 | + " movw r0, :lower16:{RCC_addr}", |
| 48 | + " movt r0, :upper16:{RCC_addr}", |
| 49 | + " ldr r1, [r0, #{RCC_AHB2ENR_offset}]", |
| 50 | + " orrs r1, #((1 << {RCC_AHB2ENR_SRAM1EN_bit}) \ |
| 51 | + | (1 << {RCC_AHB2ENR_SRAM2EN_bit}) \ |
| 52 | + | (1 << {RCC_AHB2ENR_SRAM3EN_bit}))", |
| 53 | + |
| 54 | + // Apply workaround for ST erratum 2.2.9 "Reading from AXI SRAM may lead to |
| 55 | + // data read corruption" - limits AXI SRAM read concurrency. |
| 56 | + " movw r0, :lower16:{AXI_TARG7_FN_MOD_addr}", |
| 57 | + " movt r0, :upper16:{AXI_TARG7_FN_MOD_addr}", |
| 58 | + " ldr r1, [r0]", |
| 59 | + " orrs r1, #(1 << {AXI_TARG7_FN_MOD_READ_ISS_OVERRIDE_bit})", |
| 60 | + " str r1, [r0]", |
| 61 | + |
| 62 | + // Aaaaand we're done. |
| 63 | + " bx lr", |
| 64 | + ".cfi_endproc", |
| 65 | + ".size __pre_init, . - __pre_init", |
| 66 | + |
| 67 | + PWR_addr = const 0x5802_4800, //device::PWR::ptr(), |
| 68 | + PWR_CSR1_offset = const 0x4, // reference manual 6.8.2 |
| 69 | + PWR_CSR1_ACTVOSRDY_bit = const 13, |
| 70 | + PWR_CR3_offset = const 0xC, // reference manual 6.8.4 |
| 71 | + |
| 72 | + RCC_addr = const 0x5802_4400, //device::RCC::ptr(), |
| 73 | + RCC_AHB2ENR_offset = const 0x0DC, // reference manual 8.7.42 |
| 74 | + RCC_AHB2ENR_SRAM1EN_bit = const 29, // reference manual 8.7.42 |
| 75 | + RCC_AHB2ENR_SRAM2EN_bit = const 30, // reference manual 8.7.42 |
| 76 | + RCC_AHB2ENR_SRAM3EN_bit = const 31, // reference manual 8.7.42 |
| 77 | + |
| 78 | + |
| 79 | + // The offset from the AXI block to this register is too large to do the |
| 80 | + // same base/displacement thing as the other peripherals above, so this |
| 81 | + // constant is the result of adding the base address from the reference |
| 82 | + // manual (0x5100_0000) to the offset from table 6. |
| 83 | + AXI_TARG7_FN_MOD_addr = const 0x5100_8108, |
| 84 | + AXI_TARG7_FN_MOD_READ_ISS_OVERRIDE_bit = const 0, // same |
65 | 85 | } |
66 | 86 |
|
67 | 87 | pub struct ClockConfig { |
|
0 commit comments