Contiki 2.6

xmem.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2006, Swedish Institute of Computer Science
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the Institute nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  *
00029  * @(#)$Id: xmem.c,v 1.13 2011/01/18 14:03:55 nvt-se Exp $
00030  */
00031 
00032 /**
00033  * \file
00034  *         Device driver for the ST M25P80 40MHz 1Mbyte external memory.
00035  * \author
00036  *         Björn Grönvall <bg@sics.se>
00037  *
00038  *         Data is written bit inverted (~-operator) to flash so that
00039  *         unwritten data will read as zeros (UNIX style).
00040  */
00041 
00042 
00043 #include "contiki.h"
00044 #include <stdio.h>
00045 #include <string.h>
00046 
00047 #include "dev/spi.h"
00048 #include "dev/xmem.h"
00049 #include "dev/watchdog.h"
00050 
00051 #if 0
00052 #define PRINTF(...) printf(__VA_ARGS__)
00053 #else
00054 #define PRINTF(...) do {} while (0)
00055 #endif
00056 
00057 #define  SPI_FLASH_INS_WREN        0x06
00058 #define  SPI_FLASH_INS_WRDI        0x04
00059 #define  SPI_FLASH_INS_RDSR        0x05
00060 #define  SPI_FLASH_INS_WRSR        0x01
00061 #define  SPI_FLASH_INS_READ        0x03
00062 #define  SPI_FLASH_INS_FAST_READ   0x0b
00063 #define  SPI_FLASH_INS_PP          0x02
00064 #define  SPI_FLASH_INS_SE          0xd8
00065 #define  SPI_FLASH_INS_BE          0xc7
00066 #define  SPI_FLASH_INS_DP          0xb9
00067 #define  SPI_FLASH_INS_RES         0xab
00068 /*---------------------------------------------------------------------------*/
00069 static void
00070 write_enable(void)
00071 {
00072   int s;
00073 
00074   s = splhigh();
00075   SPI_FLASH_ENABLE();
00076   
00077   SPI_WRITE(SPI_FLASH_INS_WREN);
00078 
00079   SPI_FLASH_DISABLE();
00080   splx(s);
00081 }
00082 /*---------------------------------------------------------------------------*/
00083 static unsigned
00084 read_status_register(void)
00085 {
00086   unsigned char u;
00087 
00088   int s;
00089 
00090   s = splhigh();
00091   SPI_FLASH_ENABLE();
00092   
00093   SPI_WRITE(SPI_FLASH_INS_RDSR);
00094 
00095   SPI_FLUSH();
00096   SPI_READ(u);
00097 
00098   SPI_FLASH_DISABLE();
00099   splx(s);
00100 
00101   return u;
00102 }
00103 /*---------------------------------------------------------------------------*/
00104 /*
00105  * Wait for a write/erase operation to finish.
00106  */
00107 static unsigned
00108 wait_ready(void)
00109 {
00110   unsigned u;
00111   do {
00112     u = read_status_register();
00113     watchdog_periodic();
00114   } while(u & 0x01);            /* WIP=1, write in progress */
00115   return u;
00116 }
00117 /*---------------------------------------------------------------------------*/
00118 /*
00119  * Erase 64k bytes of data. It takes about 1s before WIP goes low!
00120  */
00121 static void
00122 erase_sector(unsigned long offset)
00123 {
00124   int s;
00125 
00126   wait_ready();
00127   write_enable();
00128 
00129   s = splhigh();
00130   SPI_FLASH_ENABLE();
00131   
00132   SPI_WRITE_FAST(SPI_FLASH_INS_SE);
00133   SPI_WRITE_FAST(offset >> 16); /* MSB */
00134   SPI_WRITE_FAST(offset >> 8);
00135   SPI_WRITE_FAST(offset >> 0);  /* LSB */
00136   SPI_WAITFORTx_ENDED();
00137 
00138   SPI_FLASH_DISABLE();
00139   splx(s);
00140 }
00141 /*---------------------------------------------------------------------------*/
00142 /*
00143  * Initialize external flash *and* SPI bus!
00144  */
00145 void
00146 xmem_init(void)
00147 {
00148   int s;
00149   spi_init();
00150 
00151   P4DIR |= BV(FLASH_CS) | BV(FLASH_HOLD) | BV(FLASH_PWR);
00152   P4OUT |= BV(FLASH_PWR);       /* P4.3 Output, turn on power! */
00153 
00154   /* Release from Deep Power-down */
00155   s = splhigh();
00156   SPI_FLASH_ENABLE();
00157   SPI_WRITE_FAST(SPI_FLASH_INS_RES);
00158   SPI_WAITFORTx_ENDED();
00159   SPI_FLASH_DISABLE();          /* Unselect flash. */
00160   splx(s);
00161 
00162   SPI_FLASH_UNHOLD();
00163 }
00164 /*---------------------------------------------------------------------------*/
00165 int
00166 xmem_pread(void *_p, int size, unsigned long offset)
00167 {
00168   unsigned char *p = _p;
00169   const unsigned char *end = p + size;
00170   int s;
00171 
00172   wait_ready();
00173 
00174   ENERGEST_ON(ENERGEST_TYPE_FLASH_READ);
00175 
00176   s = splhigh();
00177   SPI_FLASH_ENABLE();
00178 
00179   SPI_WRITE_FAST(SPI_FLASH_INS_READ);
00180   SPI_WRITE_FAST(offset >> 16); /* MSB */
00181   SPI_WRITE_FAST(offset >> 8);
00182   SPI_WRITE_FAST(offset >> 0);  /* LSB */
00183   SPI_WAITFORTx_ENDED();
00184   
00185   SPI_FLUSH();
00186   for(; p < end; p++) {
00187     unsigned char u;
00188     SPI_READ(u);
00189     *p = ~u;
00190   }
00191 
00192   SPI_FLASH_DISABLE();
00193   splx(s);
00194 
00195   ENERGEST_OFF(ENERGEST_TYPE_FLASH_READ);
00196 
00197   return size;
00198 }
00199 /*---------------------------------------------------------------------------*/
00200 static const unsigned char *
00201 program_page(unsigned long offset, const unsigned char *p, int nbytes)
00202 {
00203   const unsigned char *end = p + nbytes;
00204   int s;
00205 
00206   wait_ready();
00207   write_enable();
00208 
00209   s = splhigh();
00210   SPI_FLASH_ENABLE();
00211   
00212   SPI_WRITE_FAST(SPI_FLASH_INS_PP);
00213   SPI_WRITE_FAST(offset >> 16); /* MSB */
00214   SPI_WRITE_FAST(offset >> 8);
00215   SPI_WRITE_FAST(offset >> 0);  /* LSB */
00216 
00217   for(; p < end; p++) {
00218     SPI_WRITE_FAST(~*p);
00219   }
00220   SPI_WAITFORTx_ENDED();
00221 
00222   SPI_FLASH_DISABLE();
00223   splx(s);
00224 
00225   return p;
00226 }
00227 /*---------------------------------------------------------------------------*/
00228 int
00229 xmem_pwrite(const void *_buf, int size, unsigned long addr)
00230 {
00231   const unsigned char *p = _buf;
00232   const unsigned long end = addr + size;
00233   unsigned long i, next_page;
00234 
00235   ENERGEST_ON(ENERGEST_TYPE_FLASH_WRITE);
00236 
00237   for(i = addr; i < end;) {
00238     next_page = (i | 0xff) + 1;
00239     if(next_page > end) {
00240       next_page = end;
00241     }
00242     p = program_page(i, p, next_page - i);
00243     i = next_page;
00244   }
00245 
00246   ENERGEST_OFF(ENERGEST_TYPE_FLASH_WRITE);
00247 
00248   return size;
00249 }
00250 /*---------------------------------------------------------------------------*/
00251 int
00252 xmem_erase(long size, unsigned long addr)
00253 {
00254   unsigned long end = addr + size;
00255 
00256   if(size % XMEM_ERASE_UNIT_SIZE != 0) {
00257     PRINTF("xmem_erase: bad size\n");
00258     return -1;
00259   }
00260 
00261   if(addr % XMEM_ERASE_UNIT_SIZE != 0) {
00262     PRINTF("xmem_erase: bad offset\n");
00263     return -1;
00264   }
00265 
00266   for (; addr < end; addr += XMEM_ERASE_UNIT_SIZE) {
00267     erase_sector(addr);
00268   }
00269 
00270   return size;
00271 }
00272 /*---------------------------------------------------------------------------*/