lowrisc/
flash_ctrl.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Flash Controller
6
7use core::cell::Cell;
8use core::ops::{Index, IndexMut};
9use kernel::hil;
10use kernel::utilities::cells::OptionalCell;
11use kernel::utilities::cells::TakeCell;
12use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
13use kernel::utilities::registers::{
14    register_bitfields, register_structs, ReadOnly, ReadWrite, WriteOnly,
15};
16use kernel::utilities::StaticRef;
17use kernel::ErrorCode;
18
19register_structs! {
20    pub FlashCtrlRegisters {
21        (0x000 => intr_state: ReadWrite<u32, INTR::Register>),
22        (0x004 => intr_enable: ReadWrite<u32, INTR::Register>),
23        (0x008 => intr_test: WriteOnly<u32, INTR::Register>),
24        (0x00C => alert_test: WriteOnly<u32>),
25        (0x010 => disable: ReadWrite<u32>),
26        (0x014 => exec: ReadWrite<u32>),
27        (0x018 => init: ReadWrite<u32, INIT::Register>),
28        (0x01C => ctrl_regwen: ReadOnly<u32, CTRL_REGWEN::Register>),
29        (0x020 => control: ReadWrite<u32, CONTROL::Register>),
30        (0x024 => addr: ReadWrite<u32, ADDR::Register>),
31        (0x028 => prog_type_en: ReadWrite<u32, PROG_TYPE_EN::Register>),
32        (0x02c => erase_suspend: ReadWrite<u32, ERASE_SUSPEND::Register>),
33        (0x030 => region_cfg_regwen: [ReadWrite<u32, REGION_CFG_REGWEN::Register>; 8]),
34        (0x050 => mp_region_cfg: [ReadWrite<u32, MP_REGION_CFG::Register>; 8]),
35        (0x070 => mp_region: [ReadWrite<u32, MP_REGION::Register>; 8]),
36        (0x090 => default_region: ReadWrite<u32, DEFAULT_REGION::Register>),
37
38        (0x094 => bank0_info0_regwen: [ReadWrite<u32, BANK_INFO_REGWEN::Register>; 10]),
39        (0x0BC => bank0_info0_page_cfg: [ReadWrite<u32, BANK_INFO_PAGE_CFG::Register>; 10]),
40        (0x0E4 => bank0_info1_regwen: ReadWrite<u32, BANK_INFO_REGWEN::Register>),
41        (0x0E8 => bank0_info1_page_cfg: ReadWrite<u32, BANK_INFO_PAGE_CFG::Register>),
42        (0x0EC => bank0_info2_regwen: [ReadWrite<u32, BANK_INFO_REGWEN::Register>; 2]),
43        (0x0F4 => bank0_info2_page_cfg: [ReadWrite<u32, BANK_INFO_PAGE_CFG::Register>; 2]),
44
45        (0x0FC => bank1_info0_regwen: [ReadWrite<u32, BANK_INFO_REGWEN::Register>; 10]),
46        (0x124 => bank1_info0_page_cfg: [ReadWrite<u32, BANK_INFO_PAGE_CFG::Register>; 10]),
47        (0x14C => bank1_info1_regwen: ReadWrite<u32, BANK_INFO_REGWEN::Register>),
48        (0x150 => bank1_info1_page_cfg: ReadWrite<u32, BANK_INFO_PAGE_CFG::Register>),
49        (0x154 => bank1_info2_regwen: [ReadWrite<u32, BANK_INFO_REGWEN::Register>; 2]),
50        (0x15C => bank1_info2_page_cfg: [ReadWrite<u32, BANK_INFO_PAGE_CFG::Register>; 2]),
51
52        (0x164 => hw_info_cfg_override: ReadWrite<u32>),
53        (0x168 => bank_cfg_regwen: ReadWrite<u32, BANK_CFG_REGWEN::Register>),
54        (0x16C => mp_bank_cfg_shadowed: ReadWrite<u32, MP_BANK_CFG::Register>),
55        (0x170 => op_status: ReadWrite<u32, OP_STATUS::Register>),
56        (0x174 => status: ReadOnly<u32, STATUS::Register>),
57        (0x178 => debug_state: ReadOnly<u32>),
58        (0x17C => err_code: ReadWrite<u32, ERR_CODE::Register>),
59        (0x180 => std_fault_status: ReadOnly<u32>),
60        (0x184 => fault_status: ReadOnly<u32>),
61        (0x188 => err_addr: ReadOnly<u32>),
62        (0x18C => ecc_single_err_cnt: ReadOnly<u32>),
63        (0x190 => ecc_single_addr: [ReadOnly<u32>; 2]),
64        (0x198 => phy_alert_cfg: ReadOnly<u32>),
65        (0x19C => phy_status: ReadOnly<u32, PHY_STATUS::Register>),
66        (0x1A0 => scratch: ReadWrite<u32, SCRATCH::Register>),
67        (0x1A4 => fifo_lvl: ReadWrite<u32, FIFO_LVL::Register>),
68        (0x1A8 => fifo_rst: ReadWrite<u32, FIFO_RST::Register>),
69        (0x1AC => curr_fifo_lvl: WriteOnly<u32>),
70        (0x1B0 => prog_fifo: WriteOnly<u32>),
71        (0x1B4 => rd_fifo: ReadOnly<u32>),
72        (0x1B8=> @END),
73    }
74}
75
76register_bitfields![u32,
77    INTR [
78        PROG_EMPTY OFFSET(0) NUMBITS(1) [],
79        PROG_LVL OFFSET(1) NUMBITS(1) [],
80        RD_FULL OFFSET(2) NUMBITS(1) [],
81        RD_LVL OFFSET(3) NUMBITS(1) [],
82        OP_DONE OFFSET(4) NUMBITS(1) [],
83        OP_ERROR OFFSET(5) NUMBITS(1) []
84    ],
85    INIT [
86        VAL OFFSET(0) NUMBITS(1) []
87    ],
88    CTRL_REGWEN [
89        EN OFFSET(0) NUMBITS(1) []
90    ],
91    CONTROL [
92        START OFFSET(0) NUMBITS(1) [],
93        OP OFFSET(4) NUMBITS(2) [
94            READ = 0,
95            PROG = 1,
96            ERASE = 2
97        ],
98        PROG_SEL OFFSET(6) NUMBITS(1) [
99            NORMAL = 0,
100            REPAIR = 1,
101        ],
102        ERASE_SEL OFFSET(7) NUMBITS(1) [
103            PAGE = 0,
104            BANK = 1
105        ],
106        PARTITION_SEL OFFSET(8) NUMBITS(1) [
107            // data partition - this is the portion of flash that is
108            //     accessible both by the host and by the controller.
109            DATA = 0,
110            // info partition - this is the portion of flash that is
111            //     only accessible by the controller.
112            INFO = 1
113        ],
114        INFO_SEL OFFSET(9) NUMBITS(2) [],
115        NUM OFFSET(16) NUMBITS(12) []
116    ],
117    ERR_CODE [
118        OP_ERR OFFSET(0) NUMBITS(1) [],
119        MP_ERR OFFSET(1) NUMBITS(1) [],
120        RD_ERR OFFSET(2) NUMBITS(1) [],
121        PROG_ERR OFFSET(3) NUMBITS(1) [],
122        PROG_WIN_ERR OFFSET(4) NUMBITS(1) [],
123        PROG_TYPE_ERR OFFSET(5) NUMBITS(1) [],
124        UPDATE_ERR OFFSET(6) NUMBITS(1) [],
125    ],
126    PROG_TYPE_EN [
127        NORMAL OFFSET(0) NUMBITS(1) [],
128        REPAIR OFFSET(1) NUMBITS(1) [],
129    ],
130    ERASE_SUSPEND [
131        REQ OFFSET(0) NUMBITS(1) [],
132    ],
133    ADDR [
134        START OFFSET(0) NUMBITS(32) []
135    ],
136    REGION_CFG_REGWEN [
137        REGION OFFSET(0) NUMBITS(1) [
138            // Once locked, region cannot be modified till next reset
139            Locked = 0,
140            // Region can be configured.
141            Enabled = 1,
142        ]
143    ],
144    MP_REGION_CFG [
145        // These config register fields require a special value of
146        // 0x6 (0110) to set, or 0x9 (1001) to reset
147        EN OFFSET(0) NUMBITS(4) [
148            Set = 0x6,
149            Clear = 0x9,
150        ],
151        RD_EN OFFSET(4) NUMBITS(4) [
152            Set = 0x6,
153            Clear = 0x9,
154        ],
155        PROG_EN OFFSET(8) NUMBITS(4) [
156            Set = 0x6,
157            Clear = 0x9,
158        ],
159        ERASE_EN OFFSET(12) NUMBITS(4) [
160            Set = 0x6,
161            Clear = 0x9,
162        ],
163        SCRAMBLE_EN OFFSET(16) NUMBITS(4) [
164            Set = 0x6,
165            Clear = 0x9,
166        ],
167        ECC_EN OFFSET(20) NUMBITS(4) [
168            Set = 0x6,
169            Clear = 0x9,
170        ],
171        HE_EN OFFSET(24) NUMBITS(4) [
172            Set = 0x6,
173            Clear = 0x9,
174        ],
175    ],
176    MP_REGION [
177        BASE OFFSET(0) NUMBITS(9) [],
178        SIZE OFFSET(10) NUMBITS(10) []
179    ],
180    BANK_INFO_REGWEN [
181        REGION OFFSET(0) NUMBITS(1) [
182            Locked = 0,
183            Enabled =1,
184        ]
185    ],
186    BANK_INFO_PAGE_CFG [
187        EN OFFSET(0) NUMBITS(4) [
188            Set = 0x6,
189            Clear = 0x9,
190        ],
191        RD_EN OFFSET(4) NUMBITS(4) [
192            Set = 0x6,
193            Clear = 0x9,
194        ],
195        PROG_EN OFFSET(8) NUMBITS(4) [
196            Set = 0x6,
197            Clear = 0x9,
198        ],
199        ERASE_EN OFFSET(12) NUMBITS(4) [
200            Set = 0x6,
201            Clear = 0x9,
202        ],
203        SCRAMBLE_EN OFFSET(16) NUMBITS(4) [
204            Set = 0x6,
205            Clear = 0x9,
206        ],
207        ECC_EN OFFSET(20) NUMBITS(4) [
208            Set = 0x6,
209            Clear = 0x9,
210        ],
211        HE_EN OFFSET(24) NUMBITS(4) [
212            Set = 0x6,
213            Clear = 0x9,
214        ],
215    ],
216    BANK_CFG_REGWEN [
217        BANK OFFSET(0) NUMBITS(1) []
218    ],
219    DEFAULT_REGION [
220        RD_EN OFFSET(0) NUMBITS(4) [
221            Set = 0x6,
222            Clear = 0x9,
223        ],
224        PROG_EN OFFSET(4) NUMBITS(4) [
225            Set = 0x6,
226            Clear = 0x9,
227        ],
228        ERASE_EN OFFSET(8) NUMBITS(4) [
229            Set = 0x6,
230            Clear = 0x9,
231        ],
232        SCRAMBLE_EN OFFSET(12) NUMBITS(4) [
233            Set = 0x6,
234            Clear = 0x9,
235        ],
236        ECC_EN OFFSET(16) NUMBITS(4) [
237            Set = 0x6,
238            Clear = 0x9,
239        ],
240        HE_EN OFFSET(20) NUMBITS(4) [
241            Set = 0x6,
242            Clear = 0x9,
243        ],
244    ],
245    MP_BANK_CFG [
246        ERASE_EN_0 OFFSET(0) NUMBITS(1) [],
247        ERASE_EN_1 OFFSET(1) NUMBITS(1) []
248    ],
249    OP_STATUS [
250        DONE OFFSET(0) NUMBITS(1) [],
251        ERR OFFSET(1) NUMBITS(1) []
252    ],
253    STATUS [
254        RD_FULL OFFSET(0) NUMBITS(1) [],
255        RD_EMPTY OFFSET(1) NUMBITS(1) [],
256        PROG_FULL OFFSET(2) NUMBITS(1) [],
257        PROG_EMPTY OFFSET(3) NUMBITS(1) [],
258        INIT_WIP OFFSET(4) NUMBITS(1) [],
259    ],
260    PHY_STATUS [
261        INIT_WIP OFFSET(0) NUMBITS(1) [],
262        PROG_NORMAL_AVAIL OFFSET(1) NUMBITS(1) [],
263        PROG_REPAIR_AVAIL OFFSET(2) NUMBITS(1) []
264    ],
265    SCRATCH [
266        DATA OFFSET(0) NUMBITS(32) []
267    ],
268    FIFO_LVL [
269        PROG OFFSET(0) NUMBITS(5) [],
270        RD OFFSET(8) NUMBITS(5) []
271    ],
272    FIFO_RST [
273        EN OFFSET(0) NUMBITS(1) []
274    ]
275];
276
277pub const PAGE_SIZE: usize = 2048;
278pub const FLASH_ADDR_OFFSET: usize = 0x20000000;
279pub const FLASH_WORD_SIZE: usize = 8;
280pub const FLASH_PAGES_PER_BANK: usize = 256;
281pub const FLASH_NUM_BANKS: usize = 2;
282pub const FLASH_MAX_PAGES: usize = FLASH_NUM_BANKS * FLASH_PAGES_PER_BANK;
283pub const FLASH_NUM_BUSWORDS_PER_BANK: usize = PAGE_SIZE / 4;
284pub const FLASH_MP_MAX_CFGS: usize = 8;
285// The programming windows size in words (32bit)
286pub const FLASH_PROG_WINDOW_SIZE: usize = 16;
287pub const FLASH_PROG_WINDOW_MASK: u32 = 0xFFFFFFF0;
288
289pub struct LowRiscPage(pub [u8; PAGE_SIZE]);
290
291/// Defines region permissions for flash memory protection.
292///
293/// To be used when requesting the flash controller to set
294/// specific permissions for a regions, or when reading
295/// the existing permission associated with a region.
296#[derive(PartialEq, Debug)]
297pub struct FlashMPConfig {
298    /// Region can be read.
299    pub read_en: bool,
300    /// Region can be programmed.
301    pub write_en: bool,
302    /// Region can be erased
303    pub erase_en: bool,
304    /// Region is scramble enabled
305    pub scramble_en: bool,
306    /// Region has ECC enabled
307    pub ecc_en: bool,
308    /// Region is high endurance enabled
309    pub he_en: bool,
310}
311
312impl Default for LowRiscPage {
313    fn default() -> Self {
314        Self([0; PAGE_SIZE])
315    }
316}
317
318impl Index<usize> for LowRiscPage {
319    type Output = u8;
320
321    fn index(&self, idx: usize) -> &u8 {
322        &self.0[idx]
323    }
324}
325
326impl IndexMut<usize> for LowRiscPage {
327    fn index_mut(&mut self, idx: usize) -> &mut u8 {
328        &mut self.0[idx]
329    }
330}
331
332impl AsMut<[u8]> for LowRiscPage {
333    fn as_mut(&mut self) -> &mut [u8] {
334        &mut self.0
335    }
336}
337
338#[derive(PartialEq)]
339enum FlashBank {
340    BANK0 = 0,
341    BANK1 = 1,
342}
343
344#[derive(PartialEq, Clone, Copy)]
345pub enum FlashRegion {
346    REGION0 = 0,
347    REGION1 = 1,
348    REGION2 = 2,
349    REGION3 = 3,
350    REGION4 = 4,
351    REGION5 = 5,
352    REGION6 = 6,
353    REGION7 = 7,
354}
355
356pub struct FlashCtrl<'a> {
357    registers: StaticRef<FlashCtrlRegisters>,
358    flash_client: OptionalCell<&'a dyn hil::flash::Client<FlashCtrl<'a>>>,
359    data_configured: Cell<bool>,
360    info_configured: Cell<bool>,
361    read_buf: TakeCell<'static, LowRiscPage>,
362    read_index: Cell<usize>,
363    write_buf: TakeCell<'static, LowRiscPage>,
364    write_index: Cell<usize>,
365    write_word_addr: Cell<usize>,
366    region_num: FlashRegion,
367}
368
369impl FlashCtrl<'_> {
370    pub fn new(base: StaticRef<FlashCtrlRegisters>, region_num: FlashRegion) -> Self {
371        FlashCtrl {
372            registers: base,
373            flash_client: OptionalCell::empty(),
374            data_configured: Cell::new(false),
375            info_configured: Cell::new(false),
376            read_buf: TakeCell::empty(),
377            read_index: Cell::new(0),
378            write_buf: TakeCell::empty(),
379            write_index: Cell::new(0),
380            write_word_addr: Cell::new(0),
381            region_num,
382        }
383    }
384
385    fn enable_interrupts(&self) {
386        // Enable relevent interrupts
387        self.registers.intr_enable.write(
388            INTR::PROG_EMPTY::SET
389                + INTR::PROG_LVL::CLEAR
390                + INTR::RD_FULL::CLEAR
391                + INTR::RD_LVL::SET
392                + INTR::OP_DONE::SET
393                + INTR::OP_ERROR::SET,
394        );
395    }
396
397    fn disable_interrupts(&self) {
398        // Disable and clear all interrupts
399        self.registers.intr_enable.set(0x00);
400        self.registers.intr_state.set(0xFFFF_FFFF);
401    }
402
403    /// Calculates and returns the max num words that can be programmed without
404    /// crossing the programming resolution window boundaries, which
405    /// occur at every FLASH_PROG_WINDOW_SIZE words. Note, when setting
406    /// the CONTROL::NUM, write (ret_val - 1).
407    fn calculate_max_prog_len(&self, word_addr: u32, rem_bytes: u32) -> u32 {
408        // Calculate and return the max window limit possible for this transaction in words
409        let window_limit =
410            ((word_addr + FLASH_PROG_WINDOW_SIZE as u32) & FLASH_PROG_WINDOW_MASK) - word_addr;
411        let words_to_write = rem_bytes / 4;
412
413        if words_to_write < window_limit {
414            words_to_write
415        } else {
416            window_limit
417        }
418    }
419
420    fn configure_data_partition(&self, num: FlashRegion) -> Result<(), ErrorCode> {
421        self.registers.default_region.write(
422            DEFAULT_REGION::RD_EN::Set
423                + DEFAULT_REGION::PROG_EN::Set
424                + DEFAULT_REGION::ERASE_EN::Set,
425        );
426
427        if let Some(mp_region_cfg) = self.registers.mp_region_cfg.get(num as usize) {
428            mp_region_cfg.write(
429                MP_REGION_CFG::RD_EN::Set
430                    + MP_REGION_CFG::PROG_EN::Set
431                    + MP_REGION_CFG::ERASE_EN::Set
432                    + MP_REGION_CFG::SCRAMBLE_EN::Clear
433                    + MP_REGION_CFG::ECC_EN::Clear
434                    + MP_REGION_CFG::EN::Clear,
435            );
436
437            if let Some(mp_region) = self.registers.mp_region.get(num as usize) {
438                // Size and base are stored in different registers
439                mp_region.write(
440                    MP_REGION::BASE.val(FLASH_PAGES_PER_BANK as u32) + MP_REGION::SIZE.val(0x1),
441                );
442            } else {
443                return Err(ErrorCode::INVAL);
444            }
445
446            // Enable MP Region
447            mp_region_cfg.modify(MP_REGION_CFG::EN::Set);
448        } else {
449            return Err(ErrorCode::INVAL);
450        }
451
452        self.data_configured.set(true);
453        Ok(())
454    }
455
456    fn configure_info_partition(&self, bank: FlashBank, num: FlashRegion) -> Result<(), ErrorCode> {
457        if bank == FlashBank::BANK0 {
458            if let Some(bank0_info0_page_cfg) =
459                self.registers.bank0_info0_page_cfg.get(num as usize)
460            {
461                bank0_info0_page_cfg.write(
462                    BANK_INFO_PAGE_CFG::RD_EN::Set
463                        + BANK_INFO_PAGE_CFG::PROG_EN::Set
464                        + BANK_INFO_PAGE_CFG::ERASE_EN::Set
465                        + BANK_INFO_PAGE_CFG::SCRAMBLE_EN::Set
466                        + BANK_INFO_PAGE_CFG::ECC_EN::Set
467                        + BANK_INFO_PAGE_CFG::EN::Clear,
468                );
469                bank0_info0_page_cfg.modify(BANK_INFO_PAGE_CFG::EN::Set);
470            } else {
471                return Err(ErrorCode::INVAL);
472            }
473        } else if bank == FlashBank::BANK1 {
474            if let Some(bank1_info0_page_cfg) =
475                self.registers.bank1_info0_page_cfg.get(num as usize)
476            {
477                bank1_info0_page_cfg.write(
478                    BANK_INFO_PAGE_CFG::RD_EN::Set
479                        + BANK_INFO_PAGE_CFG::PROG_EN::Set
480                        + BANK_INFO_PAGE_CFG::ERASE_EN::Set
481                        + BANK_INFO_PAGE_CFG::SCRAMBLE_EN::Set
482                        + BANK_INFO_PAGE_CFG::ECC_EN::Set
483                        + BANK_INFO_PAGE_CFG::EN::Clear,
484                );
485                bank1_info0_page_cfg.modify(BANK_INFO_PAGE_CFG::EN::Set);
486            } else {
487                return Err(ErrorCode::INVAL);
488            }
489        } else {
490            return Err(ErrorCode::INVAL);
491        }
492        self.info_configured.set(true);
493        Ok(())
494    }
495
496    /// Reset the internal FIFOs, used for when recovering from
497    /// errors.
498    pub fn reset_fifos(&self) {
499        // This field is active high, and will hold the FIFO
500        // in reset for as long as it is held.
501        self.registers.fifo_rst.write(FIFO_RST::EN::SET);
502        self.registers.fifo_rst.write(FIFO_RST::EN::CLEAR);
503    }
504
505    pub fn handle_interrupt(&self) {
506        let irqs = self.registers.intr_state.extract();
507        // MP faults don't seem to trigger any errors in intr_state,
508        // so lets check for them here.
509        let mp_fault = self.registers.err_code.is_set(ERR_CODE::MP_ERR);
510
511        self.disable_interrupts();
512
513        if irqs.is_set(INTR::OP_ERROR) || mp_fault {
514            self.registers.op_status.set(0);
515            // RW1C Clear any pending errors
516            self.registers.err_code.set(0xFFFF_FFFF);
517            self.reset_fifos();
518
519            let read_buf = self.read_buf.take();
520            let error = if mp_fault {
521                hil::flash::Error::FlashMemoryProtectionError
522            } else {
523                hil::flash::Error::FlashError
524            };
525            if let Some(buf) = read_buf {
526                // We were doing a read
527                self.flash_client.map(move |client| {
528                    client.read_complete(buf, Err(error));
529                });
530            }
531
532            let write_buf = self.write_buf.take();
533            if let Some(buf) = write_buf {
534                // We were doing a write
535                self.flash_client.map(move |client| {
536                    client.write_complete(buf, Err(error));
537                });
538            }
539
540            if self.registers.control.matches_all(CONTROL::OP::ERASE) {
541                // We were doing an erase
542                self.flash_client.map(move |client| {
543                    client.erase_complete(Err(error));
544                });
545            }
546        }
547
548        if irqs.is_set(INTR::RD_LVL) {
549            self.read_buf.map(|buf| {
550                while !self.registers.status.is_set(STATUS::RD_EMPTY)
551                    && self.read_index.get() < PAGE_SIZE
552                {
553                    let data = self.registers.rd_fifo.get().to_ne_bytes();
554                    let buf_offset = self.read_index.get();
555
556                    buf[buf_offset] = data[0];
557                    buf[buf_offset + 1] = data[1];
558                    buf[buf_offset + 2] = data[2];
559                    buf[buf_offset + 3] = data[3];
560
561                    self.read_index.set(buf_offset + 4);
562                }
563                self.enable_interrupts();
564            });
565        }
566
567        if irqs.is_set(INTR::PROG_EMPTY) {
568            self.write_buf.map(|buf| {
569                let transaction_word_len = self.calculate_max_prog_len(
570                    self.write_word_addr.get() as u32,
571                    (buf.0.len() - self.write_index.get()) as u32,
572                );
573
574                let mut words_written = 0;
575
576                // Issue program command to the controller
577                self.registers.control.write(
578                    CONTROL::OP::PROG
579                        + CONTROL::PARTITION_SEL::DATA
580                        + CONTROL::INFO_SEL::CLEAR
581                        + CONTROL::NUM.val(transaction_word_len - 1)
582                        + CONTROL::START::CLEAR,
583                );
584
585                // Set the address
586                self.registers
587                    .addr
588                    .write(ADDR::START.val(self.write_word_addr.get().saturating_mul(4) as u32));
589
590                // Start the transaction
591                self.registers.control.modify(CONTROL::START::SET);
592
593                for i in 0..transaction_word_len {
594                    if self.registers.status.is_set(STATUS::PROG_FULL) {
595                        words_written = i;
596                        break;
597                    }
598                    let buf_offset = self.write_index.get();
599                    let data: u32 = buf[buf_offset] as u32
600                        | (buf[buf_offset + 1] as u32) << 8
601                        | (buf[buf_offset + 2] as u32) << 16
602                        | (buf[buf_offset + 3] as u32) << 24;
603
604                    self.registers.prog_fifo.set(data);
605
606                    self.write_index.set(buf_offset + 4);
607                    // loop only semi-inclusive
608                    words_written = i + 1;
609                }
610
611                self.write_word_addr
612                    .set(self.write_word_addr.get() + words_written as usize);
613                self.enable_interrupts();
614            });
615        }
616
617        if irqs.is_set(INTR::OP_DONE) {
618            if self.registers.control.matches_all(CONTROL::OP::READ) {
619                let read_buf = self.read_buf.take();
620                if let Some(buf) = read_buf {
621                    // We were doing a read
622                    if self.read_index.get() >= buf.0.len() {
623                        self.registers.op_status.set(0);
624                        // We have all of the data, call the client
625                        self.flash_client.map(move |client| {
626                            client.read_complete(buf, Ok(()));
627                        });
628                    } else {
629                        // Still waiting on data, keep waiting
630                        self.read_buf.replace(buf);
631                        self.enable_interrupts();
632                    }
633                }
634            } else if self.registers.control.matches_all(CONTROL::OP::PROG) {
635                let write_buf = self.write_buf.take();
636                if let Some(buf) = write_buf {
637                    // We were doing a write
638                    if self.write_index.get() >= buf.0.len() {
639                        self.registers.op_status.set(0);
640                        // We sent all of the data, call the client
641                        self.flash_client.map(move |client| {
642                            client.write_complete(buf, Ok(()));
643                        });
644                    } else {
645                        // Still writing data, keep trying
646                        self.write_buf.replace(buf);
647                        self.enable_interrupts();
648                    }
649                }
650            } else if self.registers.control.matches_all(CONTROL::OP::ERASE) {
651                self.flash_client.map(move |client| {
652                    client.erase_complete(Ok(()));
653                });
654            }
655        }
656    }
657
658    // *** Public API for interfacing to flash memory protection ***
659
660    /// Helper function to convert an address space into page numbers for the flash controller.
661    ///
662    /// Returns `Ok(start_page_num, n_pages_used)` on successful conversion
663    /// Returns `Returns [`NOSUPPORT`](ErrorCode::NOSUPPORT)` if address space is not supported
664    ///     or is out of supported bounds
665    ///
666    /// # Arguments
667    ///
668    /// * `start_addr`  - Starting address to be converted to a page number
669    ///   Note: This is the absolute address, i.e `FLASH_ADDR_OFFSET` and
670    ///   onwards
671    /// * `end_addr`    - End address to be converted to a page number Note:
672    ///   This is the absolute address, i.e `FLASH_ADDR_OFFSET` and onwards
673    fn mp_addr_to_page_range(
674        &self,
675        mut start_addr: usize,
676        mut end_addr: usize,
677    ) -> Result<(usize, usize), ErrorCode> {
678        if start_addr >= end_addr {
679            return Err(ErrorCode::NOSUPPORT);
680        }
681
682        // Offset Absolute addresses into flash relative addresses
683        // i.e 0x20000000 -> 0x00, where 0x00 is the first word in Bank 0, Page 0.
684        if let Some(addr) = start_addr.checked_sub(FLASH_ADDR_OFFSET) {
685            start_addr = addr;
686        } else {
687            return Err(ErrorCode::NOSUPPORT);
688        }
689        if let Some(addr) = end_addr.checked_sub(FLASH_ADDR_OFFSET) {
690            end_addr = addr;
691        } else {
692            return Err(ErrorCode::NOSUPPORT);
693        }
694
695        let start_page_num = start_addr.saturating_div(PAGE_SIZE);
696        let end_page_num = end_addr.saturating_div(PAGE_SIZE);
697
698        if start_page_num >= FLASH_MAX_PAGES || end_page_num >= FLASH_MAX_PAGES {
699            // The address space does not fall within the flash layout
700            return Err(ErrorCode::NOSUPPORT);
701        }
702
703        // Find the pages utilized by the addr space, at-least one
704        // page must be used, even if both start/end addresses fall on the same page.
705        let n_pages_used: usize = end_page_num.saturating_sub(start_page_num).max(1);
706
707        // Pages numbers are 0 indexed,
708        // For example, if start_page_num is 0 and n_pages_used is 1,
709        // then the mp region is defined by page 0.
710        // If start_page_num is 0 and n_pages_used is 2, then the region is defined by pages 0 and 1.
711        Ok((start_page_num, n_pages_used))
712    }
713
714    /// Setup the specified flash memory protection configuration
715    ///
716    /// Returns `Ok(())` on successfully applying the requested configuration
717    /// Returns `[`NOSUPPORT`](ErrorCode::NOSUPPORT)` if address space is not supported,
718    ///     or the `region_num` does not exist
719    ///
720    /// # Arguments
721    ///
722    /// * `start_addr`  - Starting address that bounds the start of this region.
723    ///   Note: This is the absolute address, i.e `FLASH_ADDR_OFFSET` and
724    ///   onwards
725    /// * `end_addr`    - End address that bounds the end of this region Note:
726    ///   This is the absolute address, i.e `FLASH_ADDR_OFFSET` and onwards
727    /// * `region_num`  - The configuration region number associated with this
728    ///   region. This associates the specified permissions to a configuration
729    ///   region, the number of simultaneous configs supported should be
730    ///   requested by `mp_get_num_regions()`
731    /// * `mp_perms`    - Specifies the permissions to set
732    ///
733    /// # Examples
734    ///
735    /// Usage:
736    ///
737    /// ```ignore
738    /// peripherals
739    ///     .flash_ctrl
740    ///     .mp_set_region_perms(0x0, text_end_addr as usize, 5, &mp_cfg)
741    /// ```
742    ///
743    /// The snippet reads as:
744    /// Allow access controls as specified by mp_cfg, for the address space bounded by
745    /// `start_addr=0x0` to `end_addr=text_end_addr` and associate this cfg to `region_num=5`.
746    ///
747    /// If a user would want to modify this region (assuming it wasn't locked), you can index into it with the
748    /// associated `region_num`, in this case `5`.
749    pub fn mp_set_region_perms(
750        &self,
751        start_addr: usize,
752        end_addr: usize,
753        region_num: usize,
754        mp_perms: &FlashMPConfig,
755    ) -> Result<(), ErrorCode> {
756        let (page_number, num_pages) = self.mp_addr_to_page_range(start_addr, end_addr)?;
757
758        if region_num > FlashRegion::REGION7 as usize || page_number >= FLASH_MAX_PAGES {
759            return Err(ErrorCode::NOSUPPORT);
760        }
761
762        // Number of pages exceeds the number of remaining pages from `page_number`
763        if num_pages > FLASH_MAX_PAGES - page_number {
764            return Err(ErrorCode::NOSUPPORT);
765        }
766
767        let regs = self.registers;
768
769        if !regs.region_cfg_regwen[region_num].is_set(REGION_CFG_REGWEN::REGION) {
770            // Region locked, cannot modify until next reset
771            return Err(ErrorCode::NOSUPPORT);
772        }
773
774        // Clear any existing permissions (reset state)
775        self.registers.mp_region_cfg[region_num].write(
776            MP_REGION_CFG::EN::Clear
777                + MP_REGION_CFG::RD_EN::Clear
778                + MP_REGION_CFG::PROG_EN::Clear
779                + MP_REGION_CFG::ERASE_EN::Clear
780                + MP_REGION_CFG::SCRAMBLE_EN::Clear
781                + MP_REGION_CFG::ECC_EN::Clear
782                + MP_REGION_CFG::HE_EN::Clear,
783        );
784
785        // Set the specified permissions
786        if mp_perms.read_en {
787            self.registers.mp_region_cfg[region_num].modify(MP_REGION_CFG::RD_EN::Set);
788        }
789
790        if mp_perms.write_en {
791            self.registers.mp_region_cfg[region_num].modify(MP_REGION_CFG::PROG_EN::Set);
792        }
793
794        if mp_perms.erase_en {
795            self.registers.mp_region_cfg[region_num].modify(MP_REGION_CFG::ERASE_EN::Set);
796        }
797
798        if mp_perms.scramble_en {
799            self.registers.mp_region_cfg[region_num].modify(MP_REGION_CFG::SCRAMBLE_EN::Set);
800        }
801
802        if mp_perms.ecc_en {
803            self.registers.mp_region_cfg[region_num].modify(MP_REGION_CFG::ECC_EN::Set);
804        }
805
806        if mp_perms.he_en {
807            self.registers.mp_region_cfg[region_num].modify(MP_REGION_CFG::HE_EN::Set);
808        }
809
810        // Set the page-range for the cfg to be set
811        // For example, if base is 0 and size is 1, then the region is defined by page 0.
812        // If base is 0 and size is 2, then the region is defined by pages 0 and 1.
813        regs.mp_region[region_num]
814            .write(MP_REGION::BASE.val(page_number as u32) + MP_REGION::SIZE.val(num_pages as u32));
815
816        // Activate protection region with specified permissions
817        self.registers.mp_region_cfg[region_num].modify(MP_REGION_CFG::EN::Set);
818
819        Ok(())
820    }
821
822    /// Read the flash memory protection configuration bounded by the specified region
823    ///
824    /// Returns `[`FlashMPConfig`](lowrisc::flash_ctrl::FlashMPConfig)` on success, with the permissions
825    ///     specified by this region
826    /// Returns `[`NOSUPPORT`](ErrorCode::NOSUPPORT)` if the `region_num` does not exist
827    ///
828    /// # Arguments
829    ///
830    /// * `region_num`  - The configuration region number associated with this
831    ///   region. This associates the specified permissions to a configuration
832    ///   region.
833    pub fn mp_read_region_perms(&self, region_num: usize) -> Result<FlashMPConfig, ErrorCode> {
834        if region_num > FlashRegion::REGION7 as usize {
835            return Err(ErrorCode::NOSUPPORT);
836        }
837
838        let mp_cfg = self.registers.mp_region_cfg[region_num].extract();
839
840        let mut cfg = FlashMPConfig {
841            read_en: false,
842            write_en: false,
843            erase_en: false,
844            scramble_en: false,
845            ecc_en: false,
846            he_en: false,
847        };
848
849        if mp_cfg.matches_all(MP_REGION_CFG::RD_EN::Set) {
850            cfg.read_en = true;
851        }
852
853        if mp_cfg.matches_all(MP_REGION_CFG::PROG_EN::Set) {
854            cfg.write_en = true;
855        }
856
857        if mp_cfg.matches_all(MP_REGION_CFG::ERASE_EN::Set) {
858            cfg.erase_en = true;
859        }
860
861        if mp_cfg.matches_all(MP_REGION_CFG::SCRAMBLE_EN::Set) {
862            cfg.scramble_en = true;
863        }
864
865        if mp_cfg.matches_all(MP_REGION_CFG::ECC_EN::Set) {
866            cfg.ecc_en = true;
867        }
868
869        if mp_cfg.matches_all(MP_REGION_CFG::HE_EN::Set) {
870            cfg.he_en = true;
871        }
872
873        Ok(cfg)
874    }
875
876    /// Get the number of configuration regions supported by this hardware
877    ///
878    /// Returns `Ok(FLASH_MP_MAX_CFGS)` where FLASH_MP_MAX_CFGS is the number of
879    ///     cfg regions supported
880    ///
881    /// Note: Indexing starts with 0, this returns the total
882    /// number of configuration registers.
883    pub fn mp_get_num_regions(&self) -> Result<u32, ErrorCode> {
884        Ok(FLASH_MP_MAX_CFGS as u32)
885    }
886
887    /// Check if the specified `region_num` is locked by hardware
888    ///
889    /// Returns `Ok(bool)` on success, if true the region is locked till next reset,
890    ///     if false, it is unlocked.
891    /// Returns `[`NOSUPPORT`](ErrorCode::NOSUPPORT)` if the `region_num` does not exist
892    ///
893    /// # Arguments
894    ///
895    /// * `region_num`  - The configuration region number associated with this
896    ///   region. This associates the specified permissions to a configuration
897    ///   region.
898    pub fn mp_is_region_locked(&self, region_num: usize) -> Result<bool, ErrorCode> {
899        if region_num > FlashRegion::REGION7 as usize {
900            return Err(ErrorCode::NOSUPPORT);
901        }
902
903        if !self.registers.region_cfg_regwen[region_num].is_set(REGION_CFG_REGWEN::REGION) {
904            // Region locked until next reset
905            return Ok(true);
906        }
907        // Region enabled and can be modified
908        Ok(false)
909    }
910
911    /// Lock the configuration
912    /// Locks the config bounded by `region_num`
913    /// such that no further modifications can be made until the next system reset.
914    ///
915    /// Returns `[`NOSUPPORT`](ErrorCode::NOSUPPORT)` if the `region_num` does not exist
916    /// Returns `[`ALREADY`](ErrorCode::ALREADY)` if the `region_num` region is already locked
917    /// Returns `Ok(())` on successfully locking the region
918    ///
919    /// # Arguments
920    ///
921    /// * `region_num`  - The configuration region number associated with this
922    ///   region. This associates the specified permissions to a configuration
923    ///   region.
924    pub fn mp_lock_region_cfg(&self, region_num: usize) -> Result<(), ErrorCode> {
925        if region_num > FlashRegion::REGION7 as usize {
926            return Err(ErrorCode::NOSUPPORT);
927        }
928
929        if !self.registers.region_cfg_regwen[region_num].is_set(REGION_CFG_REGWEN::REGION) {
930            // Region already locked
931            return Err(ErrorCode::ALREADY);
932        }
933
934        self.registers.region_cfg_regwen[region_num].write(REGION_CFG_REGWEN::REGION::Locked);
935
936        Ok(())
937    }
938}
939
940impl<C: hil::flash::Client<Self>> hil::flash::HasClient<'static, C> for FlashCtrl<'_> {
941    fn set_client(&self, client: &'static C) {
942        self.flash_client.set(client);
943    }
944}
945
946impl hil::flash::Flash for FlashCtrl<'_> {
947    type Page = LowRiscPage;
948
949    /// The flash controller will truncate to the closest, lower word aligned address.
950    /// For example, if 0x13 is supplied, the controller will perform a read at address 0x10.
951    fn read_page(
952        &self,
953        page_number: usize,
954        buf: &'static mut Self::Page,
955    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
956        if page_number >= FLASH_MAX_PAGES {
957            return Err((ErrorCode::INVAL, buf));
958        }
959
960        let addr = page_number.saturating_mul(PAGE_SIZE);
961
962        if !self.info_configured.get() {
963            // The info partitions have no default access. Specifically set up a region.
964            if let Err(e) = self.configure_info_partition(FlashBank::BANK1, self.region_num) {
965                return Err((e, buf));
966            }
967        }
968
969        if !self.data_configured.get() {
970            // If we aren't configured yet, configure now
971            if let Err(e) = self.configure_data_partition(self.region_num) {
972                return Err((e, buf));
973            }
974        }
975
976        // Enable interrupts and set the FIFO level
977        self.enable_interrupts();
978        self.registers.fifo_lvl.modify(FIFO_LVL::RD.val(0xF));
979
980        // Check control status before we commit
981        if !self.registers.ctrl_regwen.is_set(CTRL_REGWEN::EN) {
982            return Err((ErrorCode::BUSY, buf));
983        }
984
985        // Save the buffer
986        self.read_buf.replace(buf);
987        self.read_index.set(0);
988
989        // Start the transaction
990        self.registers.control.write(
991            CONTROL::OP::READ
992                + CONTROL::PARTITION_SEL::DATA
993                + CONTROL::INFO_SEL::SET
994                + CONTROL::NUM.val((FLASH_NUM_BUSWORDS_PER_BANK - 1) as u32)
995                + CONTROL::START::CLEAR,
996        );
997
998        // Set the address
999        self.registers.addr.write(ADDR::START.val(addr as u32));
1000
1001        // Start Transaction
1002        self.registers.control.modify(CONTROL::START::SET);
1003
1004        Ok(())
1005    }
1006
1007    /// The flash controller will truncate to the closest, lower word aligned address.
1008    /// For example, if 0x13 is supplied, the controller will perform a write at address 0x10.
1009    /// the controller does not have read modified write support.
1010    fn write_page(
1011        &self,
1012        page_number: usize,
1013        buf: &'static mut Self::Page,
1014    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
1015        if page_number >= FLASH_MAX_PAGES {
1016            return Err((ErrorCode::INVAL, buf));
1017        }
1018        let addr = page_number.saturating_mul(PAGE_SIZE);
1019
1020        if !self.info_configured.get() {
1021            // If we aren't configured yet, configure now
1022            // The info partitions have no default access. Specifically set up a region.
1023            if let Err(e) = self.configure_info_partition(FlashBank::BANK1, self.region_num) {
1024                return Err((e, buf));
1025            }
1026        }
1027
1028        if !self.data_configured.get() {
1029            // If we aren't configured yet, configure now
1030            if let Err(e) = self.configure_data_partition(self.region_num) {
1031                return Err((e, buf));
1032            }
1033        }
1034
1035        // Check control status before we commit
1036        if !self.registers.ctrl_regwen.is_set(CTRL_REGWEN::EN) {
1037            return Err((ErrorCode::BUSY, buf));
1038        }
1039
1040        // Writes should not cross programming resolution window boundaries, which
1041        // occur at every FLASH_PROG_WINDOW_SIZE words.
1042        let word_address = addr / 4;
1043
1044        let transaction_word_len =
1045            self.calculate_max_prog_len(word_address as u32, buf.0.len() as u32);
1046
1047        self.registers.control.write(
1048            CONTROL::OP::PROG
1049                + CONTROL::PARTITION_SEL::DATA
1050                + CONTROL::INFO_SEL::CLEAR
1051                + CONTROL::NUM.val(transaction_word_len - 1)
1052                + CONTROL::START::CLEAR,
1053        );
1054
1055        // Set the address
1056        self.registers.addr.write(ADDR::START.val(addr as u32));
1057
1058        // Reset the write index
1059        self.write_index.set(0);
1060
1061        // Start the transaction
1062        self.registers.control.modify(CONTROL::START::SET);
1063
1064        let mut words_written = 0;
1065
1066        // Write the data until we are full or have written all the data
1067        for i in 0..transaction_word_len {
1068            if self.registers.status.is_set(STATUS::PROG_FULL) {
1069                words_written = i;
1070                break;
1071            }
1072            let buf_offset = self.write_index.get();
1073            let data: u32 = buf[buf_offset] as u32
1074                | (buf[buf_offset + 1] as u32) << 8
1075                | (buf[buf_offset + 2] as u32) << 16
1076                | (buf[buf_offset + 3] as u32) << 24;
1077
1078            self.registers.prog_fifo.set(data);
1079
1080            self.write_index.set(buf_offset + 4);
1081            // loop only semi-inclusive
1082            words_written = i + 1;
1083        }
1084
1085        self.write_word_addr
1086            .set((addr / 4) + words_written as usize);
1087
1088        // Save the buffer
1089        self.write_buf.replace(buf);
1090
1091        // Enable interrupts and set the FIFO level (interrupt when fully drained)
1092        self.enable_interrupts();
1093        self.registers.fifo_lvl.modify(FIFO_LVL::PROG.val(0x00));
1094
1095        Ok(())
1096    }
1097
1098    /// The controller will truncate to the closest lower page aligned address.
1099    /// Similarly for bank erases, the controller will truncate to the closest
1100    /// lower bank aligned address.
1101    fn erase_page(&self, page_number: usize) -> Result<(), ErrorCode> {
1102        if page_number >= FLASH_MAX_PAGES {
1103            return Err(ErrorCode::INVAL);
1104        }
1105        let addr = page_number.saturating_mul(PAGE_SIZE);
1106
1107        if !self.data_configured.get() {
1108            // If we aren't configured yet, configure now
1109            self.configure_data_partition(self.region_num)?;
1110        }
1111
1112        if !self.info_configured.get() {
1113            // If we aren't configured yet, configure now
1114            self.configure_info_partition(FlashBank::BANK1, self.region_num)?;
1115        }
1116
1117        // Check control status before we commit
1118        if !self.registers.ctrl_regwen.is_set(CTRL_REGWEN::EN) {
1119            return Err(ErrorCode::BUSY);
1120        }
1121
1122        // Disable bank erase
1123        for _ in 0..2 {
1124            self.registers
1125                .mp_bank_cfg_shadowed
1126                .modify(MP_BANK_CFG::ERASE_EN_0::CLEAR + MP_BANK_CFG::ERASE_EN_1::CLEAR);
1127        }
1128
1129        // Set the address
1130        self.registers.addr.write(ADDR::START.val(addr as u32));
1131
1132        // Enable interrupts
1133        self.enable_interrupts();
1134
1135        // Start the transaction
1136        self.registers.control.write(
1137            CONTROL::OP::ERASE
1138                + CONTROL::ERASE_SEL::PAGE
1139                + CONTROL::PARTITION_SEL::DATA
1140                + CONTROL::START::SET,
1141        );
1142        Ok(())
1143    }
1144}