imxrt_boot_gen/serial_flash/nor.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
//! Serial NOR configuration blocks and fields
use crate::flexspi;
/// `ipCmdSerialClkFreq` field for serial NOR-specific FCB
///
/// Chip specific value, not used by ROM.
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum SerialClockFrequency {
/// No change, keep current serial clock unchanged
NoChange = 0,
MHz30,
MHz50,
MHz60,
#[cfg(not(any(feature = "imxrt1170", feature = "imxrt1180")))]
MHz75,
MHz80,
MHz100,
#[cfg(any(
feature = "imxrt1060",
feature = "imxrt1064",
feature = "imxrt1170",
feature = "imxrt1180"
))]
MHz120,
MHz133,
#[cfg(any(feature = "imxrt1050", feature = "imxrt1060", feature = "imxrt1064"))]
MHz166,
}
/// A serial NOR configuration block
///
/// This is the memory that you'll need to properly place in memory in order to
/// boot your i.MX RT system. Consider keeping the symbol name, and specifying
/// a link section, so that you can more easily place the memory in your linker
/// script.
///
/// Unless otherwise specified, all unset fields are set to a bitpattern of zero.
///
/// ## 1170 notes
///
/// By default, `isUniformBlockSize` is set to 1, indicating that the block size and
/// sector sizes are equal. Using `block_size` clears this field and allows you to
/// differentiate the block size from the sector size.
///
/// ```no_run
/// use imxrt_boot_gen::serial_flash::nor;
/// # use imxrt_boot_gen::flexspi::{self, LookupTable};
///
/// # const FLEXSPI_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LookupTable::new());
/// #[no_mangle]
/// #[link_section = ".serial_nor_cb"]
/// static SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =
/// nor::ConfigurationBlock::new(FLEXSPI_CONFIGURATION_BLOCK)
/// .page_size(256)
/// .sector_size(4096)
/// .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30);
/// ```
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct ConfigurationBlock {
mem_cfg: flexspi::ConfigurationBlock,
page_size: u32,
sector_size: u32,
ip_cmd_serial_clk_freq: SerialClockFrequency,
extras: Extras,
}
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
struct Imxrt11xxExtras {
is_uniform_block_size: u8,
is_data_order_swapped: u8,
_reserved0: [u8; 5],
block_size: u32,
flash_state_ctx: u32,
_reserved1: [u8; 40],
}
const _: () = assert!(55 == core::mem::size_of::<Imxrt11xxExtras>());
#[cfg(any(feature = "imxrt1170", feature = "imxrt1180"))]
type Extras = Imxrt11xxExtras;
#[cfg(not(any(feature = "imxrt1170", feature = "imxrt1180")))]
type Extras = [u8; core::mem::size_of::<Imxrt11xxExtras>()];
const fn extras() -> Extras {
#[cfg(any(feature = "imxrt1170", feature = "imxrt1180"))]
{
Extras {
// By default, signal that block size equals sector size.
is_uniform_block_size: 1u8,
is_data_order_swapped: 0u8,
_reserved0: [0u8; 5],
block_size: 0u32,
flash_state_ctx: 0u32,
_reserved1: [0u8; 40],
}
}
#[cfg(not(any(feature = "imxrt1170", feature = "imxrt1180")))]
{
[0u8; core::mem::size_of::<Imxrt11xxExtras>()]
}
}
impl ConfigurationBlock {
/// Create a new serial NOR configuration block based on the FlexSPI configuration
/// block
pub const fn new(mut mem_cfg: flexspi::ConfigurationBlock) -> Self {
mem_cfg.device_type = 1;
ConfigurationBlock {
mem_cfg,
page_size: 0,
sector_size: 0,
ip_cmd_serial_clk_freq: SerialClockFrequency::NoChange,
extras: extras(),
}
}
/// Set the serial NOR page size
pub const fn page_size(mut self, page_size: u32) -> Self {
self.page_size = page_size;
self
}
/// Set the serial NOR sector size
pub const fn sector_size(mut self, sector_size: u32) -> Self {
self.sector_size = sector_size;
self
}
/// Set the serial clock frequency
pub const fn ip_cmd_serial_clk_freq(
mut self,
serial_clock_frequency: SerialClockFrequency,
) -> Self {
self.ip_cmd_serial_clk_freq = serial_clock_frequency;
self
}
}
#[cfg(any(feature = "imxrt1170", feature = "imxrt1180"))]
impl ConfigurationBlock {
/// Set the serial NOR block size if it differs from the sector size.
///
/// By default, the configuration block signals to the hardware that the
/// sector size is the same as the block size. Calling this will override
/// that setting, allowing you to configure a different block size.
///
/// The behavior is unspecified if you call this with a block size that's
/// equal to the sector size.
pub const fn block_size(mut self, block_size: u32) -> Self {
self.extras.is_uniform_block_size = 0u8;
self.extras.block_size = block_size;
self
}
}
const _STATIC_ASSERT_SIZE: [u32; 1] =
[0; (core::mem::size_of::<ConfigurationBlock>() == 512) as usize];
#[cfg(test)]
mod test {
use super::{flexspi, ConfigurationBlock, SerialClockFrequency};
use crate::flexspi::LookupTable;
#[test]
fn smoke() {
const _CFG: ConfigurationBlock =
ConfigurationBlock::new(flexspi::ConfigurationBlock::new(LookupTable::new()))
.page_size(256)
.sector_size(4095)
.ip_cmd_serial_clk_freq(SerialClockFrequency::MHz30);
}
}