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];