imxrt_boot_gen/serial_flash/nor.rs
1//! Serial NOR configuration blocks and fields
2
3use core::num::NonZeroU8;
4
5use crate::{Imxrt, flexspi};
6
7/// The serial clock frequency for IP access.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(transparent)]
10pub struct IpSerialClockFrequency(pub(crate) NonZeroU8);
11
12impl IpSerialClockFrequency {
13 /// Returns the raw enum value for this frequency selection.
14 pub const fn get(self) -> u8 {
15 self.0.get()
16 }
17}
18
19/// A serial NOR configuration block
20///
21/// This is the memory that you'll need to properly place in memory in order to
22/// boot your i.MX RT system. Consider keeping the symbol name, and specifying
23/// a link section, so that you can more easily place the memory in your linker
24/// script.
25///
26/// Unless otherwise specified, all unset fields are set to a bitpattern of zero.
27///
28/// ## 1160, 1170, and 1180 notes
29///
30/// By default, `isUniformBlockSize` is set to 1, indicating that the block size and
31/// sector sizes are equal. Using `block_size` clears this field and allows you to
32/// differentiate the block size from the sector size.
33///
34/// ```no_run
35/// use imxrt_boot_gen::serial_flash::nor;
36/// use imxrt_boot_gen::{Imxrt, flexspi::SerialClockOption};
37/// # use imxrt_boot_gen::flexspi::{self, LookupTable};
38///
39/// const CHIP: Imxrt = // ...
40/// # Imxrt::Imxrt1170;
41///
42/// # const FLEXSPI_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LookupTable::new());
43/// #[unsafe(no_mangle)]
44/// #[unsafe(link_section = ".serial_nor_cb")]
45/// static SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =
46/// nor::ConfigurationBlock::new(CHIP, FLEXSPI_CONFIGURATION_BLOCK)
47/// .page_size(256)
48/// .sector_size(4096)
49/// .ip_cmd_serial_clk_freq(Some(CHIP.ip_serial_clock_frequency(SerialClockOption::MHz30)));
50/// ```
51#[derive(Debug, Clone, Copy)]
52#[repr(C, packed)]
53pub struct ConfigurationBlock {
54 mem_cfg: flexspi::ConfigurationBlock,
55 page_size: u32,
56 sector_size: u32,
57 ip_cmd_serial_clk_freq: Option<IpSerialClockFrequency>,
58 extras: Extras,
59}
60
61/// Extra configurations available on some MCUs.
62#[derive(Debug, Clone, Copy)]
63#[repr(C, packed)]
64pub struct Extras {
65 is_uniform_block_size: u8,
66 is_data_order_swapped: u8,
67 _reserved0: [u8; 5],
68 block_size: u32,
69 flash_state_ctx: u32,
70 _reserved1: [u8; 40],
71}
72
73const _: () = assert!(55 == core::mem::size_of::<Extras>());
74
75const fn extras(imxrt: Imxrt) -> Extras {
76 use Imxrt::*;
77 Extras {
78 // By default, signal that block size equals sector size.
79 // Only do this on supported chips.
80 is_uniform_block_size: matches!(imxrt, Imxrt1160 | Imxrt1170 | Imxrt1180) as u8,
81 is_data_order_swapped: 0u8,
82 _reserved0: [0u8; 5],
83 block_size: 0u32,
84 flash_state_ctx: 0u32,
85 _reserved1: [0u8; 40],
86 }
87}
88
89impl ConfigurationBlock {
90 /// Create a new serial NOR configuration block based on the FlexSPI configuration
91 /// block
92 pub const fn new(imxrt: Imxrt, mut mem_cfg: flexspi::ConfigurationBlock) -> Self {
93 mem_cfg.device_type = 1;
94 ConfigurationBlock {
95 mem_cfg,
96 page_size: 0,
97 sector_size: 0,
98 ip_cmd_serial_clk_freq: None,
99 extras: extras(imxrt),
100 }
101 }
102 /// Set the serial NOR page size
103 pub const fn page_size(mut self, page_size: u32) -> Self {
104 self.page_size = page_size;
105 self
106 }
107 /// Set the serial NOR sector size
108 pub const fn sector_size(mut self, sector_size: u32) -> Self {
109 self.sector_size = sector_size;
110 self
111 }
112 /// Set the serial clock frequency for IP access.
113 ///
114 /// Use `None` to indicate no change from the default frequency.
115 pub const fn ip_cmd_serial_clk_freq(
116 mut self,
117 serial_clock_frequency: Option<IpSerialClockFrequency>,
118 ) -> Self {
119 self.ip_cmd_serial_clk_freq = serial_clock_frequency;
120 self
121 }
122
123 /// Return extra configurations that are available on the "bigger" parts.
124 pub const fn extras(&mut self, imxrt: Imxrt) -> Option<&mut Extras> {
125 match imxrt {
126 Imxrt::Imxrt1160 | Imxrt::Imxrt1170 | Imxrt::Imxrt1180 => Some(&mut self.extras),
127 _ => None,
128 }
129 }
130}
131
132impl Extras {
133 /// Set the serial NOR block size if it differs from the sector size.
134 ///
135 /// By default, the configuration block signals to the hardware that the
136 /// sector size is the same as the block size. Calling this will override
137 /// that setting, allowing you to configure a different block size.
138 ///
139 /// The behavior is unspecified if you call this with a block size that's
140 /// equal to the sector size.
141 pub const fn block_size(&mut self, block_size: u32) -> &mut Self {
142 self.is_uniform_block_size = 0u8;
143 self.block_size = block_size;
144 self
145 }
146}
147
148const _STATIC_ASSERT_SIZE: [u32; 1] =
149 [0; (core::mem::size_of::<ConfigurationBlock>() == 512) as usize];