common: Implement Mx25l::erase_all().
[gps-watch.git] / src / common / mx25l.rs
index 6e74f4ac006bf52dfae59b808882f0981e21e3ac..388d31172497b858c86c2ac36731156d1d16e2ed 100644 (file)
 
 use gpio;
 use spi;
+use storage::{Storage, Error};
+
+const SECTOR_SIZE: usize = 4096;
+const PAGE_SIZE:   usize = 256;
 
 pub struct Mx25l {
     cs_gpio: u32,
@@ -30,10 +34,17 @@ pub struct Mx25l {
 }
 
 enum Command {
+    PP   = 0x02,
+    READ = 0x03,
     RDSR = 0x05,
+    WREN = 0x06,
+    SE   = 0x20,
+    CE   = 0x60,
     RDID = 0x9f,
 }
 
+const SR_WIP: u8 = 1 << 0;
+
 impl Mx25l {
     pub fn new(cs_gpio: u32, cs_gpio_pin: u32) -> Mx25l {
         Mx25l {
@@ -62,6 +73,76 @@ impl Mx25l {
         })
     }
 
+    pub fn erase_all(&self) {
+        self.write_enable();
+
+        self.with_selected(|| {
+            spi::tx8(spi::SPI0, Command::CE as u8);
+        });
+
+        while self.write_in_progress() {
+        }
+    }
+
+    pub fn erase_sector(&self, address: usize) -> Result<(), Error> {
+        if (address & (SECTOR_SIZE - 1)) != 0 {
+            return Err(Error::UnalignedAddress);
+        }
+
+        self.write_enable();
+
+        self.with_selected(|| {
+            spi::tx8(spi::SPI0, Command::SE as u8);
+
+            spi::tx8(spi::SPI0, (address >> 16) as u8);
+            spi::tx8(spi::SPI0, (address >>  8) as u8);
+            spi::tx8(spi::SPI0, (address >>  0) as u8);
+        });
+
+        while self.write_in_progress() {
+        }
+
+        Ok(())
+    }
+
+    pub fn program_page(&self, address: usize, buffer: &[u8; PAGE_SIZE])
+                        -> Result<(), Error> {
+        if (address & (PAGE_SIZE - 1)) != 0 {
+            return Err(Error::UnalignedAddress);
+        }
+
+        if buffer.iter().all(|&b| b == 0xff) {
+            return Ok(());
+        }
+
+        self.with_selected(|| {
+            spi::tx8(spi::SPI0, Command::PP as u8);
+
+            spi::tx8(spi::SPI0, (address >> 16) as u8);
+            spi::tx8(spi::SPI0, (address >>  8) as u8);
+            spi::tx8(spi::SPI0, (address >>  0) as u8);
+
+            for &b in buffer.iter() {
+                spi::tx8(spi::SPI0, b);
+            }
+        });
+
+        while self.write_in_progress() {
+        }
+
+        Ok(())
+    }
+
+    fn write_enable(&self) {
+        self.with_selected(|| {
+            spi::tx8(spi::SPI0, Command::WREN as u8);
+        });
+    }
+
+    fn write_in_progress(&self) -> bool {
+        (self.read_status() & SR_WIP) != 0
+    }
+
     fn with_selected<F, T>(&self, func: F) -> T
         where F: FnOnce() -> T
     {
@@ -74,3 +155,40 @@ impl Mx25l {
         r
     }
 }
+
+impl Storage for Mx25l {
+    fn read(&self, address: usize, buffer: &mut [u8]) {
+        self.with_selected(|| {
+            spi::tx8(spi::SPI0, Command::READ as u8);
+
+            spi::tx8(spi::SPI0, (address >> 16) as u8);
+            spi::tx8(spi::SPI0, (address >>  8) as u8);
+            spi::tx8(spi::SPI0, (address >>  0) as u8);
+
+            for i in 0..buffer.len() {
+                buffer[i] = spi::tx8(spi::SPI0, 0xff);
+            }
+        })
+    }
+
+    fn write(&mut self, address: usize, buffer: &[u8; SECTOR_SIZE])
+             -> Result<(), Error> {
+        if let Err(e) = self.erase_sector(address) {
+            return Err(e);
+        }
+
+        for (i, page_bytes) in buffer.chunks(PAGE_SIZE).enumerate() {
+            let page_address = address + (i * PAGE_SIZE);
+
+            // XXX: Inefficient.
+            let mut ba = [0xff; PAGE_SIZE];
+            ba.copy_from_slice(&page_bytes);
+
+            if let Err(e) = self.program_page(page_address, &ba) {
+                return Err(e);
+            }
+        }
+
+        Ok(())
+    }
+}