bootloader: Add the flash module.
[gps-watch.git] / src / bootloader / flash.rs
1 /*
2  * Copyright (c) 2019 Tilman Sauerbeck (tilman at code-monkey de)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 use common::register;
25
26 type Reg8 = register::Register<u8>;
27
28 const FTFA_BASE: u32 = 0x40020000;
29
30 const FTFA_FSTAT : u32 = FTFA_BASE + 0x00;
31 const FTFA_FCCOB3: u32 = FTFA_BASE + 0x04;
32 const FTFA_FCCOB2: u32 = FTFA_BASE + 0x05;
33 const FTFA_FCCOB1: u32 = FTFA_BASE + 0x06;
34 const FTFA_FCCOB0: u32 = FTFA_BASE + 0x07;
35 const FTFA_FCCOB7: u32 = FTFA_BASE + 0x08;
36 const FTFA_FCCOB6: u32 = FTFA_BASE + 0x09;
37 const FTFA_FCCOB5: u32 = FTFA_BASE + 0x0a;
38 const FTFA_FCCOB4: u32 = FTFA_BASE + 0x0b;
39
40 const FTFA_FSTAT_MGSTAT0 : u8 = 1 << 0;
41 const FTFA_FSTAT_FPVIOL  : u8 = 1 << 4;
42 const FTFA_FSTAT_ACCERR  : u8 = 1 << 5;
43 const FTFA_FSTAT_RDCOLERR: u8 = 1 << 6;
44 const FTFA_FSTAT_CCIF    : u8 = 1 << 7;
45
46 const FCMD_RD1SEC : u8 = 0x01;
47 const FCMD_PGM4   : u8 = 0x06;
48 const FCMD_ERSSCR : u8 = 0x09;
49
50 pub const SECTOR_SIZE : usize = 1024;
51
52 // The hardware refuses to run another command (or even take the new command's
53 // parameters) until the errors reported by a previous command have been
54 // acknowledged (see KL26P121M48SF4RM.pdf p. 445).
55 fn prepare_command() {
56     let mut fstat = Reg8::new(FTFA_FSTAT);
57
58     fstat.write(FTFA_FSTAT_FPVIOL | FTFA_FSTAT_ACCERR | FTFA_FSTAT_RDCOLERR);
59 }
60
61 fn run_command() -> u8 {
62     let mut fstat = Reg8::new(FTFA_FSTAT);
63
64     // Start command.
65     fstat.write(FTFA_FSTAT_CCIF);
66
67     // Wait for it to finish.
68     loop {
69         let v = fstat.read();
70
71         if (v & FTFA_FSTAT_CCIF) != 0 {
72             return v;
73         }
74     }
75 }
76
77 fn erase_check(sector: u32) -> bool {
78     let address = sector as usize * SECTOR_SIZE;
79     let num_words = SECTOR_SIZE / 4;
80
81     prepare_command();
82
83     Reg8::new(FTFA_FCCOB0).write(FCMD_RD1SEC);
84     Reg8::new(FTFA_FCCOB1).write((address >> 16) as u8);
85     Reg8::new(FTFA_FCCOB2).write((address >>  8) as u8);
86     Reg8::new(FTFA_FCCOB3).write((address >>  0) as u8);
87     Reg8::new(FTFA_FCCOB4).write((num_words >> 8) as u8);
88     Reg8::new(FTFA_FCCOB5).write((num_words >> 0) as u8);
89     Reg8::new(FTFA_FCCOB6).write(0);
90
91     (run_command() & FTFA_FSTAT_MGSTAT0) == 0
92 }
93
94 pub fn erase(sector: u32) {
95     // Already erased?
96     if erase_check(sector) {
97         return;
98     }
99
100     let address = sector as usize * SECTOR_SIZE;
101
102     prepare_command();
103
104     Reg8::new(FTFA_FCCOB0).write(FCMD_ERSSCR);
105     Reg8::new(FTFA_FCCOB1).write((address >> 16) as u8);
106     Reg8::new(FTFA_FCCOB2).write((address >>  8) as u8);
107     Reg8::new(FTFA_FCCOB3).write((address >>  0) as u8);
108
109     run_command();
110 }
111
112 pub fn program(sector: u32, data: &[u8]) {
113     prepare_command();
114
115     for (i, bytes) in data.chunks(4).enumerate() {
116         if bytes[0] == 0xff &&
117            bytes[1] == 0xff &&
118            bytes[2] == 0xff &&
119            bytes[3] == 0xff {
120             continue;
121         }
122
123         let address = (sector as usize * SECTOR_SIZE) + (i * 4);
124
125         Reg8::new(FTFA_FCCOB0).write(FCMD_PGM4);
126         Reg8::new(FTFA_FCCOB1).write((address >> 16) as u8);
127         Reg8::new(FTFA_FCCOB2).write((address >>  8) as u8);
128         Reg8::new(FTFA_FCCOB3).write((address >>  0) as u8);
129         Reg8::new(FTFA_FCCOB4).write(bytes[3]);
130         Reg8::new(FTFA_FCCOB5).write(bytes[2]);
131         Reg8::new(FTFA_FCCOB6).write(bytes[1]);
132         Reg8::new(FTFA_FCCOB7).write(bytes[0]);
133
134         run_command();
135     }
136 }
137
138 pub fn verify(sector: u32, data: &[u8]) -> (u32, u32) {
139     let mut num_mismatches = 0;
140     let mut first_mismatch_offset = 0;
141
142     for i in (0..data.len()).rev() {
143         let address = (sector as usize * SECTOR_SIZE) + i;
144         let ptr = address as *const u8;
145         let actual_byte = unsafe { core::ptr::read(ptr) };
146         let expected_byte = data[i];
147
148         if actual_byte != expected_byte {
149             num_mismatches += 1;
150             first_mismatch_offset = i;
151         }
152     }
153
154     (num_mismatches, first_mismatch_offset as u32)
155 }