common: Implement Mx25l::erase_sector().
[gps-watch.git] / src / common / mx25l.rs
1 /*
2  * Copyright (c) 2020 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 gpio;
25 use spi;
26 use storage::Storage;
27
28 const SECTOR_SIZE: usize = 4096;
29
30 pub struct Mx25l {
31     cs_gpio: u32,
32     cs_gpio_pin: u32,
33 }
34
35 enum Command {
36     READ = 0x03,
37     RDSR = 0x05,
38     WREN = 0x06,
39     SE   = 0x20,
40     RDID = 0x9f,
41 }
42
43 pub enum Error {
44     UnalignedAddress = 1,
45 }
46
47 const SR_WIP: u8 = 1 << 0;
48
49 impl Mx25l {
50     pub fn new(cs_gpio: u32, cs_gpio_pin: u32) -> Mx25l {
51         Mx25l {
52             cs_gpio: cs_gpio,
53             cs_gpio_pin: cs_gpio_pin,
54         }
55     }
56
57     pub fn read_status(&self) -> u8 {
58         self.with_selected(|| {
59             spi::tx8(spi::SPI0, Command::RDSR as u8);
60
61             spi::tx8(spi::SPI0, 0xff)
62         })
63     }
64
65     pub fn read_id(&self) -> (u8, u16) {
66         self.with_selected(|| {
67             spi::tx8(spi::SPI0, Command::RDID as u8);
68
69             let manufacturer_id = spi::tx8(spi::SPI0, 0xff);
70             let device_id0 = spi::tx8(spi::SPI0, 0xff) as u16;
71             let device_id1 = spi::tx8(spi::SPI0, 0xff) as u16;
72
73             (manufacturer_id, device_id0 | (device_id1 << 8))
74         })
75     }
76
77     pub fn erase_sector(&self, address: usize) -> Result<(), Error> {
78         if (address & (SECTOR_SIZE - 1)) != 0 {
79             return Err(Error::UnalignedAddress);
80         }
81
82         self.write_enable();
83
84         self.with_selected(|| {
85             spi::tx8(spi::SPI0, Command::SE as u8);
86
87             spi::tx8(spi::SPI0, (address >> 16) as u8);
88             spi::tx8(spi::SPI0, (address >>  8) as u8);
89             spi::tx8(spi::SPI0, (address >>  0) as u8);
90         });
91
92         while self.write_in_progress() {
93         }
94
95         Ok(())
96     }
97
98     fn write_enable(&self) {
99         self.with_selected(|| {
100             spi::tx8(spi::SPI0, Command::WREN as u8);
101         });
102     }
103
104     fn write_in_progress(&self) -> bool {
105         (self.read_status() & SR_WIP) != 0
106     }
107
108     fn with_selected<F, T>(&self, func: F) -> T
109         where F: FnOnce() -> T
110     {
111         gpio::clear(self.cs_gpio, self.cs_gpio_pin);
112
113         let r = func();
114
115         gpio::set(self.cs_gpio, self.cs_gpio_pin);
116
117         r
118     }
119 }
120
121 impl Storage for Mx25l {
122     fn read(&self, address: usize, buffer: &mut [u8]) {
123         self.with_selected(|| {
124             spi::tx8(spi::SPI0, Command::READ as u8);
125
126             spi::tx8(spi::SPI0, (address >> 16) as u8);
127             spi::tx8(spi::SPI0, (address >>  8) as u8);
128             spi::tx8(spi::SPI0, (address >>  0) as u8);
129
130             for i in 0..buffer.len() {
131                 buffer[i] = spi::tx8(spi::SPI0, 0xff);
132             }
133         })
134     }
135 }