]> git.kernelconcepts.de Git - karo-tx-redboot.git/blobdiff - packages/devs/flash/arm/at91/v2_0/src/at91_flash.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / devs / flash / arm / at91 / v2_0 / src / at91_flash.c
index f9c3d6798cce126b7ee105f9805e7339e2dedded..93f6b4e6cf44df3ae9267d1cf020f8f5e63a575c 100644 (file)
@@ -2,13 +2,16 @@
 //
 //      at91_flash.c
 //
-//      Flash programming
+//      Flash programming for the at91 devices which have the 
+//      Embedded Flash Controller.
 //
 //==========================================================================
 //####ECOSGPLCOPYRIGHTBEGIN####
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2006 eCosCentric Ltd
+// Copyright (C) 2006 Andrew Lunn (andrew.lunn@ascom.ch)
 //
 // eCos is free software; you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free
 //
 // This exception does not invalidate any other reasons why a work based on
 // this file might be covered by the GNU General Public License.
-//
-// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
-// at http://sources.redhat.com/ecos/ecos-license/
 // -------------------------------------------
 //####ECOSGPLCOPYRIGHTEND####
 //==========================================================================
 //#####DESCRIPTIONBEGIN####
 //
 // Author(s):    gthomas
-// Contributors: gthomas
-// Date:         2001-07-17
+// Contributors: gthomas, dmoseley, Andrew Lunn, Oliver Munz
+// Date:         2000-07-26
 // Purpose:      
 // Description:  
 //              
 //==========================================================================
 
 #include <pkgconf/hal.h>
-#include <cyg/hal/hal_arch.h>
-#include <cyg/hal/hal_cache.h>
-#include <cyg/hal/hal_io.h>
+#include <pkgconf/devs_flash_at91.h>
+
+#include <cyg/hal/hal_io.h>             
+#include <cyg/hal/hal_intr.h>
+#include <cyg/infra/cyg_ass.h>
 
 #define  _FLASH_PRIVATE_
 #include <cyg/io/flash.h>
 
-#include "flash.h"
+#include <string.h>
 
-#define _si(p) ((p[0]<<8)|p[1])
+#define FLASH_TIMEOUT       100000
 
-int
-flash_hwr_init(void)
-{
-    unsigned short data[4];
-    extern char flash_query, flash_query_end;
-    typedef int code_fun(unsigned char *);
-    code_fun *_flash_query;
-    int code_len, stat, num_regions, region_size;
-
-    // Copy 'program' code to RAM for execution
-    code_len = (unsigned long)&flash_query_end - (unsigned long)&flash_query;
-    _flash_query = (code_fun *)flash_info.work_space;
-    memcpy(_flash_query, &flash_query, code_len);
-    HAL_DCACHE_SYNC();  // Should guarantee this code will run
-
-    stat = (*_flash_query)(data);
-#if 0
-    (*flash_info.pf)("stat = %x\n", stat);
-    dump_buf(data, sizeof(data));
+#ifdef CYGBLD_DEV_FLASH_AT91_LOCKING
+static cyg_uint32 sector_size;
 #endif
 
-    if (data[0] != FLASH_Atmel_code) {
-        (*flash_info.pf)("Not Atmel = %x\n", data[0]);
-        return FLASH_ERR_HWR;
-    }
+// Disable the flash controller from erasing the page before
+// programming it
+static void 
+flash_erase_before_write_disable (void)
+{
+  cyg_uint32 fmr;
+  
+  HAL_READ_UINT32(AT91_MC+AT91_MC_FMR, fmr);
+  fmr = fmr | AT91_MC_FMR_NEBP;
+  HAL_WRITE_UINT32(AT91_MC+AT91_MC_FMR, fmr);
+}
 
-    if (data[1] == (unsigned short)FLASH_ATMEL_29LV1024) {
-        num_regions = 256;
-        region_size = 0x100;
-    } else {
-        (*flash_info.pf)("Unknown device type: %x\n", data[1]);
-        return FLASH_ERR_HWR;
+// Enable the flash controller to erase the page before programming
+// it
+static void 
+flash_erase_before_write_enable (void) 
+{
+  
+  cyg_uint32 fmr;
+
+  HAL_READ_UINT32(AT91_MC+AT91_MC_FMR, fmr);
+  fmr = fmr & ~((cyg_uint32) AT91_MC_FMR_NEBP);
+  HAL_WRITE_UINT32(AT91_MC+AT91_MC_FMR, fmr);
+}
+
+// Is the flash controller ready to accept the next command?
+static __inline__ cyg_bool 
+flash_controller_is_ready(void) 
+CYGBLD_ATTRIB_SECTION(".2ram.flash_run_command");
+
+static __inline__ cyg_bool 
+flash_controller_is_ready(void) 
+{
+  cyg_uint32 fsr;
+  
+  HAL_READ_UINT32(AT91_MC+AT91_MC_FSR, fsr);
+  return (fsr & AT91_MC_FSR_FRDY ? true : false);
+}
+
+// Busy loop waiting for the controller to finish the command.
+// Wait a maximum of timeout loops and then return an error.
+static __inline__ int 
+flash_wait_for_controller (cyg_uint32 timeout) 
+CYGBLD_ATTRIB_SECTION(".2ram.flash_run_command");
+
+static __inline__ int 
+flash_wait_for_controller (cyg_uint32 timeout)
+{
+  while (!flash_controller_is_ready()){
+    timeout--;
+    if (!timeout) {
+      return FLASH_ERR_DRV_TIMEOUT;
     }
+  }
+  return FLASH_ERR_OK;
+}
+
+// Execute one command on the flash controller. This code should
+// probably not be in flash
+
+static int 
+flash_run_command(cyg_uint32 address, 
+                  cyg_uint32 command, 
+                  cyg_uint32 timeout) 
+CYGBLD_ATTRIB_SECTION(".2ram.flash_run_command");
+
+static int 
+flash_run_command(cyg_uint32 address, 
+                  cyg_uint32 command, 
+                  cyg_uint32 timeout) 
+{
+  cyg_uint32 retcode;
+  cyg_uint32 fsr;
+  cyg_uint32 mask;
+  cyg_uint32 page;
+  
+  page = ((cyg_uint32) address - (cyg_uint32) flash_info.start) / 
+    flash_info.block_size;
+  
+  // Wait for the last command to finish
+  retcode = flash_wait_for_controller(timeout);
+  if (retcode != FLASH_ERR_OK){
+    return retcode;
+  }
+  
+  HAL_DISABLE_INTERRUPTS(mask);
+  
+  HAL_WRITE_UINT32(AT91_MC+AT91_MC_FCR, 
+                   command |
+                   ((page & AT91_MC_FCR_PAGE_MASK) << AT91_MC_FCR_PAGE_SHIFT) |
+                   AT91_MC_FCR_KEY);
+
+  retcode = flash_wait_for_controller(timeout);
+
+  HAL_RESTORE_INTERRUPTS(mask);
+
+  if (retcode != FLASH_ERR_OK){
+    return retcode;
+  }
+
+  // Check for an error
+  HAL_READ_UINT32(AT91_MC+AT91_MC_FSR, fsr);
+
+  if ((fsr & AT91_MC_FSR_LOCKE) == AT91_MC_FSR_LOCKE)
+    return FLASH_ERR_PROTECT;
+  if ((fsr & AT91_MC_FSR_PROGE) == AT91_MC_FSR_PROGE)
+    return FLASH_ERR_PROGRAM;
 
-    // Hard wired for now
-    flash_info.block_size = region_size;
-    flash_info.blocks = num_regions;
-    flash_info.start = (void *)0x01010000;
-    flash_info.end = (void *)(0x01010000+(num_regions*region_size));
+  return FLASH_ERR_OK;
+}
+
+// The flash is embedded in the CPU package. So return the chip
+// ID. This allows us to determine if the chip is one we support and
+// the size of the flash
+int flash_query(void *data) 
+{
+  cyg_uint32 chipID1r;
+  
+  HAL_READ_UINT32(AT91_DBG+AT91_DBG_C1R, chipID1r);
+  
+  memcpy(data, &chipID1r, sizeof(chipID1r));
+  return FLASH_ERR_OK;
+}
+
+// Initialize the hardware. Make sure we have a flash device we know
+// how to program and determine its size, the size of the blocks, and
+// the number of blocks. The query function returns the chip ID 1
+// register which tells us about the CPU we are running on, the flash
+// size etc. Use this information to determine we have a valid setup.
+int 
+flash_hwr_init(void){
+
+  cyg_uint32 chipID1r;
+  cyg_uint32 flash_mode;
+  cyg_uint8  fmcn;
+  cyg_uint32 lock_bits;
+  
+  flash_query (&chipID1r);
+
+  if ((chipID1r & AT91_DBG_C1R_CPU_MASK) != AT91_DBG_C1R_ARM7TDMI)
+    goto out;
+
+  if (((chipID1r & AT91_DBG_C1R_ARCH_MASK) != AT91_DBG_C1R_ARCH_AT91SAM7Sxx) &&
+      ((chipID1r & AT91_DBG_C1R_ARCH_MASK) != AT91_DBG_C1R_ARCH_AT91SAM7Xxx))
+    goto out;
+  
+  if ((chipID1r & AT91_DBG_C1R_FLASH_MASK) == AT91_DBG_C1R_FLASH_0K)
+    goto out;
+  
+  switch (chipID1r & AT91_DBG_C1R_FLASH_MASK) {
+    case AT91_DBG_C1R_FLASH_32K:
+      flash_info.block_size = 128;
+      flash_info.blocks = 256;
+      lock_bits = 8;
+      break;
+    case AT91_DBG_C1R_FLASH_64K:
+      flash_info.block_size = 128;
+      flash_info.blocks = 512;
+      lock_bits = 16;
+      break;
+    case AT91_DBG_C1R_FLASH_128K:
+      flash_info.block_size = 256;
+      flash_info.blocks = 512;
+      lock_bits = 8;
+      break;
+    case AT91_DBG_C1R_FLASH_256K:
+      flash_info.block_size = 256;
+      flash_info.blocks = 1024;
+      lock_bits = 16;
+      break;
+    default:
+      goto out;
+  }
+  flash_info.buffer_size = 0;
+  flash_info.start = (void *) 0x00100000;
+  flash_info.end = (void *)(((cyg_uint32) flash_info.start) + 
+                            flash_info.block_size * flash_info.blocks);
+#ifdef CYGBLD_DEV_FLASH_AT91_LOCKING
+  sector_size = flash_info.block_size * flash_info.blocks / lock_bits;
+#endif
+  // Set the FLASH clock to 1.5 microseconds based on the MCLK.  This
+  // assumes the CPU is still running from the PLL clock as defined in
+  // the HAL CDL and the HAL startup code. 
+  fmcn = CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / 1000000 * 1.5;
+  HAL_READ_UINT32(AT91_MC+AT91_MC_FMR, flash_mode);
+  flash_mode = flash_mode & ~AT91_MC_FMR_FMCN_MASK;
+  flash_mode = flash_mode | (fmcn << AT91_MC_FMR_FMCN_SHIFT);
+  HAL_WRITE_UINT32(AT91_MC+AT91_MC_FMR, flash_mode);
+  
+  return FLASH_ERR_OK;
+  
+ out:
+  (*flash_info.pf)("Can't identify FLASH, sorry, ChipID1 %x\n",
+                   chipID1r );
+  return FLASH_ERR_HWR;
+}
+
+// Erase a block. The flash controller does not have a command to
+// erase a block. So instead we setup the controller to do a program
+// writing all 0xff with an erase operation first.
+int 
+flash_erase_block (volatile unsigned long block) 
+{
+  cyg_uint32 retcode;
+  cyg_uint32 *buffer;
+  cyg_uint32 *end;
+  
+  buffer = (cyg_uint32 *) block;
+  end = (cyg_uint32 *) (block + flash_info.block_size);
+  
+  while (buffer < end){ 
+    *buffer = (cyg_uint32) 0xffffffff;
+    buffer++;
+  }
+  
+  flash_erase_before_write_enable();
+  retcode = flash_run_command(block, 
+                              AT91_MC_FCR_START_PROG, 
+                              FLASH_TIMEOUT);
+  
+  return retcode;
+}
+
+// Write into the flash. The datasheet says that performing 8 or 16bit
+// accesses results in unpredictable corruption. So the current code
+// checks that these conditions are upheld. It would be possible to
+// perform extra reads and masking operation to support writing to
+// none word assigned addresses or not multiple or a word length.
+int 
+flash_program_buf (volatile unsigned long addr, unsigned long *data, int len)
+{
+  cyg_uint32 retcode;
+  volatile unsigned long *target;
+  
+  CYG_ASSERT(len % 4 == 0, "Only word writes allowed by current code");
+  CYG_ASSERT(addr % 4 == 0, "Address must be word aligned for current code");
+  
+  target = (volatile unsigned long *)addr;
+  
+  while (len > 0) {
+    *target = *data;
+    data++;
+    target++;
+    len = len - sizeof(unsigned long);
+  }
+  
+  flash_erase_before_write_disable();
+  retcode = flash_run_command(addr, 
+                              AT91_MC_FCR_START_PROG, 
+                              FLASH_TIMEOUT);
+  
+  return retcode;
+}
+
+#ifdef CYGBLD_DEV_FLASH_AT91_LOCKING
+// Unlock a block. This is not strictly possible, we can only lock and
+// unlock sectors. This will unlock the sector which contains the
+// block.
+int
+flash_unlock_block(volatile unsigned long block, int block_size, int blocks)
+{
+  cyg_uint32 sector;
+  cyg_uint32 retcode;
+  cyg_uint32 status;
+  
+  sector = (((cyg_uint32) block) - (cyg_uint32) flash_info.start) / 
+    sector_size;
+  HAL_READ_UINT32(AT91_MC + AT91_MC_FSR, status);
+  
+  if (status & (1 << (sector + 16))){
+      retcode = flash_run_command(block, 
+                                  AT91_MC_FCR_UNLOCK, 
+                                  FLASH_TIMEOUT);
+      return retcode;
+  } else {
     return FLASH_ERR_OK;
+  }
 }
 
-// Map a hardware status to a package error
+// Lock a block. This is not strictly possible, we can only lock and
+// unlock sectors. This will lock the sector which contains the
+// block.
 int
-flash_hwr_map_error(int err)
+flash_lock_block(volatile unsigned long block, int block_size, int blocks)
 {
-    if (err) {
-        (*flash_info.pf)("Err = %x\n", err);
-        return FLASH_ERR_PROGRAM;
-    } else {
-        return FLASH_ERR_OK;
-    }
+  cyg_uint32 sector;
+  cyg_uint32 retcode;
+  cyg_uint32 status;
+  
+  sector = (((cyg_uint32) block) - (cyg_uint32) flash_info.start) / 
+    sector_size;
+
+  HAL_READ_UINT32(AT91_MC + AT91_MC_FSR, status);
+  
+  if (!(status & (1 << (sector + 16)))){
+      retcode = flash_run_command(block, 
+                                  AT91_MC_FCR_LOCK, 
+                                  FLASH_TIMEOUT);
+      
+      return retcode;
+  } else {
+    return FLASH_ERR_OK;
+  }
+}
+#endif 
+  
+// Map a hardware status to a package error. NOP since the errors are
+// already mapped.
+int flash_hwr_map_error(int err){
+
+  return err;
 }
 
 // See if a range of FLASH addresses overlaps currently running code
-bool
-flash_code_overlaps(void *start, void *end)
-{
+bool flash_code_overlaps(void *start, void *end){
+
     extern char _stext[], _etext[];
 
     return ((((unsigned long)&_stext >= (unsigned long)start) &&