capsules_extra/
at24c_eeprom.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 2023.
4
5//! Driver for the AT24C32/64 EEPROM memory. Built on top of the I2C interface.
6//! Provides interface for the NonvolatileToPages driver.
7//!
8//! Datasheet:
9//! <https://ww1.microchip.com/downloads/en/devicedoc/doc0336.pdf>
10//!
11//! > The AT24C32/64 provides 32,768/65,536 bits of serial electrically erasable and programmable
12//! > read only memory (EEPROM) organized as 4096/8192 words of 8 bits each. The device’s cascadable
13//! > feature allows up to 8 devices to share a common 2- wire bus. The device is optimized for use
14//! > in many industrial and commercial applications where low power and low voltage operation are
15//! > essential. The AT24C32/64 is available in space saving 8-pin JEDEC PDIP, 8-pin JEDEC SOIC,
16//! > 8-pin EIAJ SOIC, and 8-pin TSSOP (AT24C64) packages and is accessed via a 2-wire serial
17//! > interface.
18//!
19//! Usage
20//! -----
21//!
22//! ```rust,ignore
23//! let i2cmux = I2CMuxComponent::new(i2c0, None).finalize(components::i2c_mux_component_static!());
24//!
25//! let at24c_buffer = static_init!([u8; 34], [0; 34]);
26//!
27//! let at24c_i2c_device = static_init!(I2CDevice, I2CDevice::new(i2cmux, 0x50));
28//! let at24c_capsule = static_init!(capsules_extra::at24c_eeprom::AT24C,capsules_extra::at24c_eeprom::AT24C::new(
29//!             at24c_i2c_device,
30//!             at24c_buffer,
31//!         ) );
32//! at24c_i2c_device.set_client(at24c_capsule);
33//!
34//! let nonvolatile_storage = components::nonvolatile_storage::NonvolatileStorageComponent::new(
35//!         board_kernel,
36//!         capsules_extra::nonvolatile_storage_driver::DRIVER_NUM,
37//!         at24c_capsule,
38//!         0x0,
39//!         0x10000,
40//!         0x0,
41//!         0x0,
42//!     ).finalize(components::nonvolatile_storage_component_static!(capsules_extra::at24c_eeprom::AT24C));
43//! ```
44
45use core::cell::Cell;
46use core::cmp;
47
48use kernel::hil::i2c::{Error, I2CClient, I2CDevice};
49use kernel::utilities::cells::{OptionalCell, TakeCell};
50use kernel::{hil, ErrorCode};
51
52const PAGE_SIZE: usize = 32;
53
54pub struct EEPROMPage(pub [u8; PAGE_SIZE]);
55
56impl Default for EEPROMPage {
57    fn default() -> Self {
58        Self([0; PAGE_SIZE])
59    }
60}
61
62impl AsMut<[u8]> for EEPROMPage {
63    fn as_mut(&mut self) -> &mut [u8] {
64        &mut self.0
65    }
66}
67
68#[derive(Copy, Clone, Debug)]
69enum State {
70    Idle,
71    Reading,
72    Writing,
73    Erasing,
74}
75
76pub struct AT24C<'a> {
77    i2c: &'a dyn I2CDevice,
78    buffer: TakeCell<'static, [u8]>,
79    client_page: TakeCell<'a, EEPROMPage>,
80    flash_client: OptionalCell<&'a dyn hil::flash::Client<AT24C<'a>>>,
81    state: Cell<State>,
82}
83
84impl<'a> AT24C<'a> {
85    pub fn new(i2c: &'a dyn I2CDevice, buffer: &'static mut [u8]) -> Self {
86        Self {
87            i2c,
88            buffer: TakeCell::new(buffer),
89            client_page: TakeCell::empty(),
90            flash_client: OptionalCell::empty(),
91            state: Cell::new(State::Idle),
92        }
93    }
94
95    fn read_sector(
96        &self,
97        page_number: usize,
98        buf: &'static mut EEPROMPage,
99    ) -> Result<(), (ErrorCode, &'static mut EEPROMPage)> {
100        let address = page_number * PAGE_SIZE;
101        if let Some(rxbuffer) = self.buffer.take() {
102            rxbuffer[0] = ((address >> 8) & 0x00ff) as u8;
103            rxbuffer[1] = (address & 0x00ff) as u8;
104
105            self.i2c.enable();
106            self.state.set(State::Reading);
107            if let Err((error, local_buffer)) = self.i2c.write_read(rxbuffer, 2, PAGE_SIZE) {
108                self.buffer.replace(local_buffer);
109                self.i2c.disable();
110                Err((error.into(), buf))
111            } else {
112                self.client_page.replace(buf);
113                Ok(())
114            }
115        } else {
116            Err((ErrorCode::RESERVE, buf))
117        }
118    }
119
120    fn write_sector(
121        &self,
122        page_number: usize,
123        buf: &'static mut EEPROMPage,
124    ) -> Result<(), (ErrorCode, &'static mut EEPROMPage)> {
125        let address = page_number * PAGE_SIZE;
126        // Schedule page write and do first
127        if let Some(txbuffer) = self.buffer.take() {
128            txbuffer[0] = ((address >> 8) & 0x00ff) as u8;
129            txbuffer[1] = (address & 0x00ff) as u8;
130
131            let write_len = cmp::min(txbuffer.len() - 2, buf.0.len());
132
133            txbuffer[2..(write_len + 2)].copy_from_slice(&buf.0[..write_len]);
134
135            self.i2c.enable();
136            self.state.set(State::Writing);
137            if let Err((error, txbuffer)) = self.i2c.write(txbuffer, write_len + 2) {
138                self.buffer.replace(txbuffer);
139                self.i2c.disable();
140                Err((error.into(), buf))
141            } else {
142                self.client_page.replace(buf);
143                Ok(())
144            }
145        } else {
146            Err((ErrorCode::RESERVE, buf))
147        }
148    }
149
150    fn erase_sector(&self, page_number: usize) -> Result<(), ErrorCode> {
151        let address = page_number * PAGE_SIZE;
152        // Schedule page write and do first
153        if let Some(txbuffer) = self.buffer.take() {
154            txbuffer[0] = ((address >> 8) & 0x00ff) as u8;
155            txbuffer[1] = (address & 0x00ff) as u8;
156
157            let write_len = cmp::min(txbuffer.len() - 2, PAGE_SIZE);
158
159            for i in 0..write_len {
160                txbuffer[i + 2] = 0;
161            }
162
163            self.i2c.enable();
164            self.state.set(State::Erasing);
165            if let Err((error, txbuffer)) = self.i2c.write(txbuffer, write_len + 2) {
166                self.buffer.replace(txbuffer);
167                self.i2c.disable();
168                Err(error.into())
169            } else {
170                Ok(())
171            }
172        } else {
173            Err(ErrorCode::RESERVE)
174        }
175    }
176}
177
178impl I2CClient for AT24C<'static> {
179    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), Error>) {
180        match self.state.get() {
181            State::Reading => {
182                self.state.set(State::Idle);
183                self.i2c.disable();
184                if let Some(client_page) = self.client_page.take() {
185                    client_page.0[..PAGE_SIZE].copy_from_slice(&buffer[..PAGE_SIZE]);
186                    self.buffer.replace(buffer);
187                    self.flash_client.map(|client| {
188                        if status.is_err() {
189                            client.read_complete(client_page, Err(hil::flash::Error::FlashError));
190                        } else {
191                            client.read_complete(client_page, Ok(()));
192                        }
193                    });
194                }
195            }
196            State::Writing => {
197                self.state.set(State::Idle);
198                self.buffer.replace(buffer);
199                self.i2c.disable();
200                self.flash_client.map(|client| {
201                    if let Some(client_page) = self.client_page.take() {
202                        if status.is_err() {
203                            client.write_complete(client_page, Err(hil::flash::Error::FlashError));
204                        } else {
205                            client.write_complete(client_page, Ok(()));
206                        }
207                    }
208                });
209            }
210            State::Erasing => {
211                self.state.set(State::Idle);
212                self.buffer.replace(buffer);
213                self.i2c.disable();
214                self.flash_client.map(move |client| {
215                    if status.is_err() {
216                        client.erase_complete(Err(hil::flash::Error::FlashError));
217                    } else {
218                        client.erase_complete(Ok(()));
219                    }
220                });
221            }
222            State::Idle => {}
223        }
224    }
225}
226
227impl hil::flash::Flash for AT24C<'_> {
228    type Page = EEPROMPage;
229
230    fn read_page(
231        &self,
232        page_number: usize,
233        buf: &'static mut Self::Page,
234    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
235        self.read_sector(page_number, buf)
236    }
237
238    fn write_page(
239        &self,
240        page_number: usize,
241        buf: &'static mut Self::Page,
242    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
243        self.write_sector(page_number, buf)
244    }
245
246    fn erase_page(&self, page_number: usize) -> Result<(), ErrorCode> {
247        self.erase_sector(page_number)
248    }
249}
250
251impl<'a, C: hil::flash::Client<Self>> hil::flash::HasClient<'a, C> for AT24C<'a> {
252    fn set_client(&'a self, client: &'a C) {
253        self.flash_client.set(client);
254    }
255}