#ifndef CYGONCE_DEVS_FLASH_ATMEL_AT29CXXXX_INL #define CYGONCE_DEVS_FLASH_ATMEL_AT29CXXXX_INL //========================================================================== // // at29cxxxx.inl // // Atmel AT29Cxxxx series flash driver // //========================================================================== //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. // // 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 // Software Foundation; either version 2 or (at your option) any later version. // // eCos is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with eCos; if not, write to the Free Software Foundation, Inc., // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // // As a special exception, if other files instantiate templates or use macros // or inline functions from this file, or you compile this file and link it // with other works to produce a work based on this file, this file does not // by itself cause the resulting work to be covered by the GNU General Public // License. However the source code for this file must still be made available // in accordance with section (3) of the GNU General Public License. // // 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, jskov // Date: 2001-02-21 // Purpose: // Description: // // Notes: FLASH_P2V is not properly used. // Needs locking. //####DESCRIPTIONEND#### // //========================================================================== #include #include #include #include CYGHWR_MEMORY_LAYOUT_H #define _FLASH_PRIVATE_ #include //---------------------------------------------------------------------------- // Common device details. #define FLASH_Read_ID FLASHWORD( 0x90 ) #define FLASH_Read_ID_Exit FLASHWORD( 0xF0 ) #define FLASH_Reset FLASHWORD( 0xFF ) #define FLASH_Program FLASHWORD( 0xA0 ) #define FLASH_Block_Erase FLASHWORD( 0x30 ) #define FLASH_Data FLASHWORD( 0x80 ) // Data complement #define FLASH_Busy FLASHWORD( 0x40 ) // "Toggle" bit #define FLASH_Err FLASHWORD( 0x20 ) #define FLASH_Sector_Erase_Timer FLASHWORD( 0x08 ) #define FLASH_Setup_Addr1 (0x5555) #define FLASH_Setup_Addr2 (0x2AAA) #define FLASH_Setup_Code1 FLASHWORD( 0xAA ) #define FLASH_Setup_Code2 FLASHWORD( 0x55 ) #define FLASH_Setup_Erase FLASHWORD( 0x80 ) // Platform code must define the below // #define CYGNUM_FLASH_INTERLEAVE : Number of interleaved devices (in parallel) // #define CYGNUM_FLASH_SERIES : Number of devices in series // #define CYGNUM_FLASH_BASE : Address of first device // And select one of the below device variants #ifdef CYGPKG_DEVS_FLASH_ATMEL_AT29C040A # define FLASH_BLOCK_SIZE (0x10000*CYGNUM_FLASH_INTERLEAVE) # define FLASH_NUM_REGIONS (8) # define CYGNUM_FLASH_BASE_MASK (0xFFF80000u) // 512kB devices # define CYGNUM_FLASH_WIDTH (8) # define CYGNUM_FLASH_BLANK (1) # define CYGNUM_FLASH_ID_MANUFACTURER FLASHWORD(0x1F) # define CYGNUM_FLASH_ID_DEVICE FLASHWORD(0xA4) #endif #ifdef CYGPKG_DEVS_FLASH_ATMEL_AT29LV1024 # define FLASH_BLOCK_SIZE (0x100*CYGNUM_FLASH_INTERLEAVE) # define FLASH_NUM_REGIONS (512) # define CYGNUM_FLASH_BASE_MASK (0xFFFe0000u) // 128kB devices # define CYGNUM_FLASH_WIDTH (16) # define CYGNUM_FLASH_BLANK (1) # define CYGNUM_FLASH_ID_MANUFACTURER FLASHWORD(0x1F) # define CYGNUM_FLASH_ID_DEVICE FLASHWORD(0x26) #endif #define FLASH_DEVICE_SIZE (FLASH_BLOCK_SIZE*FLASH_NUM_REGIONS) #define CYGNUM_FLASH_DEVICES (CYGNUM_FLASH_INTERLEAVE*CYGNUM_FLASH_SERIES) //---------------------------------------------------------------------------- // Now that device properties are defined, include magic for defining // accessor type and constants. #include //---------------------------------------------------------------------------- // Functions that put the flash device into non-read mode must reside // in RAM. void flash_query(void* data) __attribute__ ((section (".2ram.flash_query"))); int flash_erase_block(void* block, unsigned int size) __attribute__ ((section (".2ram.flash_erase_block"))); int flash_program_buf(void* addr, void* data, int len) __attribute__ ((section (".2ram.flash_program_buf"))); //---------------------------------------------------------------------------- // Initialize driver details int flash_hwr_init(void) { flash_data_t id[2]; flash_dev_query(id); // Check that flash_id data is matching the one the driver was // configured for. if (id[0] != CYGNUM_FLASH_ID_MANUFACTURER || id[1] != CYGNUM_FLASH_ID_DEVICE) return FLASH_ERR_DRV_WRONG_PART; // Hard wired for now flash_info.block_size = FLASH_BLOCK_SIZE; flash_info.blocks = FLASH_NUM_REGIONS; flash_info.start = (void *)CYGNUM_FLASH_BASE; flash_info.end = (void *)(CYGNUM_FLASH_BASE+ (FLASH_NUM_REGIONS * FLASH_BLOCK_SIZE * CYGNUM_FLASH_SERIES)); return FLASH_ERR_OK; } //---------------------------------------------------------------------------- // Map a hardware status to a package error int flash_hwr_map_error(int e) { return e; } //---------------------------------------------------------------------------- // See if a range of FLASH addresses overlaps currently running code bool flash_code_overlaps(void *start, void *end) { extern unsigned char _stext[], _etext[]; return ((((unsigned long)&_stext >= (unsigned long)start) && ((unsigned long)&_stext < (unsigned long)end)) || (((unsigned long)&_etext >= (unsigned long)start) && ((unsigned long)&_etext < (unsigned long)end))); } //---------------------------------------------------------------------------- // Flash Query // // Only reads the manufacturer and part number codes for the first // device(s) in series. It is assumed that any devices in series // will be of the same type. void flash_query(void* data) { volatile flash_data_t *ROM; flash_data_t* id = (flash_data_t*) data; int i; ROM = (volatile flash_data_t*) CYGNUM_FLASH_BASE; ROM[FLASH_Setup_Addr1] = FLASH_Setup_Code1; ROM[FLASH_Setup_Addr2] = FLASH_Setup_Code2; ROM[FLASH_Setup_Addr1] = FLASH_Read_ID; // FIXME: 10ms delay for (i = 10000; i > 0; i--); // Manufacturers' code id[0] = ROM[0]; // Part number id[1] = ROM[1]; ROM[FLASH_Setup_Addr1] = FLASH_Setup_Code1; ROM[FLASH_Setup_Addr2] = FLASH_Setup_Code2; ROM[FLASH_Setup_Addr1] = FLASH_Read_ID_Exit; // FIXME: 10ms delay for (i = 10000; i > 0; i--); } //---------------------------------------------------------------------------- // Erase Block int flash_erase_block(void* block, unsigned int len) { volatile flash_data_t* ROM; volatile flash_data_t* addr_ptr = (volatile flash_data_t*) block; volatile flash_data_t* addr_ptr2; int res = FLASH_ERR_OK; while ((FLASH_ERR_OK == res) && (len > 0)) { int len2, i, timeout; flash_data_t state, prev_state; // Base address of device(s) being programmed. ROM = (volatile flash_data_t*)((unsigned long)block & ~(FLASH_DEVICE_SIZE-1)); // Program data [byte] - 4 step sequence ROM[FLASH_Setup_Addr1] = FLASH_Setup_Code1; ROM[FLASH_Setup_Addr2] = FLASH_Setup_Code2; ROM[FLASH_Setup_Addr1] = FLASH_Program; addr_ptr2 = addr_ptr; len2 = len; // Always load 256 times for (i = 0; i < 256; i++) { *addr_ptr2++ = FLASH_BlankValue; } // Wait for completion (bit 6 stops toggling) timeout = 5000000; prev_state = *addr_ptr & FLASH_Busy; while (true) { state = *addr_ptr & FLASH_Busy; if (prev_state == state) { break; } if (--timeout == 0) { res = FLASH_ERR_DRV_TIMEOUT; break; } prev_state = state; } // Verify loaded data bytes for (i = 0; (len > 0) && (i < 256) ;) { if (*addr_ptr != FLASH_BlankValue) { // Only update return value if erase operation was OK if (FLASH_ERR_OK == res) res = FLASH_ERR_DRV_VERIFY; break; } addr_ptr++; len -= sizeof(*addr_ptr); i += sizeof(*addr_ptr); } } return FLASH_ERR_OK; } //---------------------------------------------------------------------------- // Program Buffer int flash_program_buf(void* addr, void* data, int len) { volatile flash_data_t* ROM; volatile flash_data_t* addr_ptr = (volatile flash_data_t*) addr; volatile flash_data_t* data_ptr = (volatile flash_data_t*) data; volatile flash_data_t* addr_ptr2; volatile flash_data_t* data_ptr2; int res = FLASH_ERR_OK; #if 0 CYG_ASSERT((unsigned long)data_ptr & (sizeof(flash_data_t)-1) == 0, "Data not properly aligned"); CYG_ASSERT((unsigned long)addr_ptr & (CYGNUM_FLASH_INTERLEAVE*sizeof(flash_data_t)-1) == 0, "Addr not properly aligned (to first interleaved device)"); #endif while ((FLASH_ERR_OK == res) && (len > 0)) { int len2, i, timeout; flash_data_t state, prev_state; // Base address of device(s) being programmed. ROM = (volatile flash_data_t*)((unsigned long)addr & ~(FLASH_DEVICE_SIZE-1)); // Program data [byte] - 4 step sequence ROM[FLASH_Setup_Addr1] = FLASH_Setup_Code1; ROM[FLASH_Setup_Addr2] = FLASH_Setup_Code2; ROM[FLASH_Setup_Addr1] = FLASH_Program; addr_ptr2 = addr_ptr; data_ptr2 = data_ptr; len2 = len; // Always load 256 times for (i = 0; i < 256; i++) { if (len2 > 0) { *addr_ptr2++ = *data_ptr2++; len2 -= sizeof(*data_ptr2); } else { *addr_ptr2++ = FLASH_BlankValue; } } // Wait for completion (bit 6 stops toggling) timeout = 5000000; prev_state = *addr_ptr & FLASH_Busy; while (true) { state = *addr_ptr & FLASH_Busy; if (prev_state == state) { break; } if (--timeout == 0) { res = FLASH_ERR_DRV_TIMEOUT; break; } prev_state = state; } // Verify loaded data bytes for (i = 0; (len > 0) && (i < 256) ;) { if (*addr_ptr != *data_ptr) { // Only update return value if erase operation was OK if (FLASH_ERR_OK == res) res = FLASH_ERR_DRV_VERIFY; break; } addr_ptr++; data_ptr++; len -= sizeof(*data_ptr); i += sizeof(*data_ptr); } } // Ideally, we'd want to return not only the failure code, but also // the address/device that reported the error. return res; } #endif // CYGONCE_DEVS_FLASH_ATMEL_AT29CXXXX_INL