/* * Copyright 2014-2017 Cavium, Inc. * The contents of this file are subject to the terms of the Common Development * and Distribution License, v.1, (the "License"). * * You may not use this file except in compliance with the License. * * You can obtain a copy of the License at available * at http://opensource.org/licenses/CDDL-1.0 * * See the License for the specific language governing permissions and * limitations under the License. */ #include "lm5706.h" /******************************************************************************* * Constants. ******************************************************************************/ /* Buffered flash (Atmel: AT45DB011B) specific information */ #define SEEPROM_SHIFT_BITS 2 #define SEEPROM_PHY_PAGE_SIZE (1 << SEEPROM_SHIFT_BITS) #define SEEPROM_BYTE_ADDR_MASK (SEEPROM_PHY_PAGE_SIZE-1) #define SEEPROM_PAGE_SIZE 4 #define SEEPROM_TOTAL_SIZE 65536 #define BUFFERED_FLASH_SHIFT_BITS 9 #define BUFFERED_FLASH_PHY_PAGE_SIZE (1 << BUFFERED_FLASH_SHIFT_BITS) #define BUFFERED_FLASH_BYTE_ADDR_MASK (BUFFERED_FLASH_PHY_PAGE_SIZE-1) #define BUFFERED_FLASH_PAGE_SIZE 264 #define BUFFERED_FLASH_TOTAL_SIZE 0x21000 #define SAIFUN_FLASH_SHIFT_BITS 8 #define SAIFUN_FLASH_PHY_PAGE_SIZE (1 << SAIFUN_FLASH_SHIFT_BITS) #define SAIFUN_FLASH_BYTE_ADDR_MASK (SAIFUN_FLASH_PHY_PAGE_SIZE-1) #define SAIFUN_FLASH_PAGE_SIZE 256 #define SAIFUN_FLASH_BASE_TOTAL_SIZE 65536 #define ST_MICRO_FLASH_SHIFT_BITS 8 #define ST_MICRO_FLASH_PHY_PAGE_SIZE (1 << ST_MICRO_FLASH_SHIFT_BITS) #define ST_MICRO_FLASH_BYTE_ADDR_MASK (ST_MICRO_FLASH_PHY_PAGE_SIZE-1) #define ST_MICRO_FLASH_PAGE_SIZE 256 #define ST_MICRO_FLASH_BASE_TOTAL_SIZE 65536 #define ST_MICRO_FLASH_1MBIT 0x20000 /* NVRAM flags for nvram_write_dword and nvram_read_dword. */ #define NVRAM_FLAG_NONE 0x00 #define NVRAM_FLAG_SET_FIRST_CMD_BIT 0x01 #define NVRAM_FLAG_SET_LAST_CMD_BIT 0x02 #define NVRAM_FLAG_BUFFERED_FLASH 0x04 #define NVRAM_TIMEOUT_COUNT 30000 #define FLASH_STRAP_MASK (NVM_CFG1_FLASH_MODE | \ NVM_CFG1_BUFFER_MODE | \ NVM_CFG1_PROTECT_MODE | \ NVM_CFG1_FLASH_SIZE) #define FLASH_BACKUP_STRAP_MASK (0xf << 26) typedef struct _new_nvm_cfg_t { /* Strapping to indicate the flash type (original | backup) */ u32_t strapping; /* New configuration values */ u32_t config1; u32_t config2; u32_t config3; u32_t write1; u32_t buffered; u32_t shift_bits; u32_t page_size; u32_t addr_mask; u32_t total_size; char *name; } new_nvm_cfg_t; /* This table is indexed by the strap values */ static const new_nvm_cfg_t cfg_table[] = { /* Slow EEPROM */ {0x00000000, 0x40830380, 0x009f0081, 0xa184a053, 0xaf000400, 1, SEEPROM_SHIFT_BITS, SEEPROM_PAGE_SIZE, SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, "EEPROM - slow"}, /* Expansion entry 0001 */ {0x08000002, 0x4b808201, 0x00050081, 0x03840253, 0xaf020406, 0, SAIFUN_FLASH_SHIFT_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 0001"}, /* Saifun SA25F010 (non-buffered flash) */ {0x04000001, 0x47808201, 0x00050081, 0x03840253, 0xaf020406, /* strap, cfg1, & write1 need updates */ 0, SAIFUN_FLASH_SHIFT_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*2, "Non-buffered flash (128kB)"}, /* Saifun SA25F020 (non-buffered flash) */ {0x0c000003, 0x4f808201, 0x00050081, 0x03840253, 0xaf020406, /* strap, cfg1, & write1 need updates */ 0, SAIFUN_FLASH_SHIFT_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*4, "Non-buffered flash (256kB)"}, /* Expansion entry 0100 */ {0x11000000, 0x53808201, 0x00050081, 0x03840253, 0xaf020406, 0, SAIFUN_FLASH_SHIFT_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 0100"}, /* Entry 0101: ST M45PE10 (non-buffered flash, TetonII B0) */ {0x19000002, 0x5b808201, 0x000500db, 0x03840253, 0xaf020406, 0, ST_MICRO_FLASH_SHIFT_BITS, ST_MICRO_FLASH_PAGE_SIZE, ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*2, "Entry 0101: ST M45PE10 (128kB non-bufferred)"}, /* Entry 0110: ST M45PE20 (non-buffered flash)*/ {0x15000001, 0x57808201, 0x000500db, 0x03840253, 0xaf020406, 0, ST_MICRO_FLASH_SHIFT_BITS, ST_MICRO_FLASH_PAGE_SIZE, ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*4, "Entry 0110: ST M45PE20 (256kB non-bufferred)"}, /* Saifun SA25F005 (non-buffered flash) */ {0x1d000003, 0x5f808201, 0x00050081, 0x03840253, 0xaf020406, /* strap, cfg1, & write1 need updates */ 0, SAIFUN_FLASH_SHIFT_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE, "Non-buffered flash (64kB)"}, /* Fast EEPROM */ {0x22000000, 0x62808380, 0x009f0081, 0xa184a053, 0xaf000400, 1, SEEPROM_SHIFT_BITS, SEEPROM_PAGE_SIZE, SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, "EEPROM - fast"}, /* Expansion entry 1001 */ {0x2a000002, 0x6b808201, 0x00050081, 0x03840253, 0xaf020406, 0, SAIFUN_FLASH_SHIFT_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1001"}, /* Expansion entry 1010 */ {0x26000001, 0x67808201, 0x00050081, 0x03840253, 0xaf020406, 0, SAIFUN_FLASH_SHIFT_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1010"}, /* ATMEL AT45DB011B (buffered flash) */ {0x2e000003, 0x6e808273, 0x00570081, 0x68848353, 0xaf000400, 1, BUFFERED_FLASH_SHIFT_BITS, BUFFERED_FLASH_PAGE_SIZE, BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE, "Buffered flash (128kB)"}, /* Expansion entry 1100 */ {0x33000000, 0x73808201, 0x00050081, 0x03840253, 0xaf020406, 0, SAIFUN_FLASH_SHIFT_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1100"}, /* Expansion entry 1101 */ {0x3b000002, 0x7b808201, 0x00050081, 0x03840253, 0xaf020406, 0, SAIFUN_FLASH_SHIFT_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1101"}, /* Ateml Expansion entry 1110 */ {0x37000001, 0x76808273, 0x00570081, 0x68848353, 0xaf000400, 1, BUFFERED_FLASH_SHIFT_BITS, BUFFERED_FLASH_PAGE_SIZE, BUFFERED_FLASH_BYTE_ADDR_MASK, 0, "Entry 1110 (Atmel)"}, /* ATMEL AT45DB021B (buffered flash) */ {0x3f000003, 0x7e808273, 0x00570081, 0x68848353, 0xaf000400, 1, BUFFERED_FLASH_SHIFT_BITS, BUFFERED_FLASH_PAGE_SIZE, BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE*2, "Buffered flash (256kB)"}, }; /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC lm_status_t acquire_nvram_lock( lm_device_t *pdev) { lm_status_t lm_status; u32_t j, cnt; u32_t val; /* Adjust timeout for emulation/FPGA */ cnt = NVRAM_TIMEOUT_COUNT; if (CHIP_REV(pdev) == CHIP_REV_FPGA) cnt *= 10; else if (CHIP_REV(pdev) == CHIP_REV_IKOS) cnt *= 100; val = 0; /* Request access to the flash interface. */ REG_WR(pdev, nvm.nvm_sw_arb, NVM_SW_ARB_ARB_REQ_SET2); for(j = 0; j < cnt*10; j++) { REG_RD(pdev, nvm.nvm_sw_arb, &val); if(val & NVM_SW_ARB_ARB_ARB2) { break; } mm_wait(pdev, 5); } if(val & NVM_SW_ARB_ARB_ARB2) { lm_status = LM_STATUS_SUCCESS; } else { DbgBreakMsg("Cannot get access to nvram interface.\n"); lm_status = LM_STATUS_BUSY; } return lm_status; } /* acquire_nvram_lock */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC void release_nvram_lock( lm_device_t *pdev) { u32_t j, cnt; u32_t val; /* Relinquish nvram interface. */ REG_WR(pdev, nvm.nvm_sw_arb, NVM_SW_ARB_ARB_REQ_CLR2); /* Adjust timeout for emulation/FPGA */ cnt = NVRAM_TIMEOUT_COUNT; if (CHIP_REV(pdev) == CHIP_REV_FPGA) cnt *= 10; else if (CHIP_REV(pdev) == CHIP_REV_IKOS) cnt *= 100; val = 0; for(j = 0; j < cnt; j++) { REG_RD(pdev, nvm.nvm_sw_arb, &val); if(!(val & NVM_SW_ARB_ARB_ARB2)) { break; } mm_wait(pdev, 5); } DbgBreakIf(val & NVM_SW_ARB_ARB_ARB2); } /* release_nvram_lock */ /******************************************************************************* * Description: * * Return: * ******************************************************************************/ STATIC lm_status_t enable_nvram_write( lm_device_t *pdev) { u32_t val, j, cnt; lm_status_t lm_status; REG_RD(pdev, misc.misc_cfg, &val); REG_WR(pdev, misc.misc_cfg, val | MISC_CFG_NVM_WR_EN_PCI); lm_status = LM_STATUS_SUCCESS; if (!pdev->hw_info.flash_spec.buffered) { REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_DONE); REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_WREN | NVM_COMMAND_DOIT); /* Adjust timeout for emulation/FPGA */ cnt = NVRAM_TIMEOUT_COUNT; if (CHIP_REV(pdev) == CHIP_REV_FPGA) cnt *= 10; else if (CHIP_REV(pdev) == CHIP_REV_IKOS) cnt *= 100; lm_status = LM_STATUS_BUSY; for(j = 0; j < cnt; j++) { mm_wait(pdev, 5); REG_RD(pdev, nvm.nvm_command, &val); if(val & NVM_COMMAND_DONE) { lm_status = LM_STATUS_SUCCESS; break; } } } return lm_status; } /* enable_nvram_write */ /******************************************************************************* * Description: * * Return: * ******************************************************************************/ STATIC lm_status_t disable_nvram_write( lm_device_t *pdev) { lm_status_t lm_status; u32_t val; REG_RD(pdev, misc.misc_cfg, &val); REG_WR(pdev, misc.misc_cfg, val & ~MISC_CFG_NVM_WR_EN); lm_status = LM_STATUS_SUCCESS; #if 0 /* On Saifun and ST parts, WP kicks in at the end of the write. So, no need to have this. */ if (!pdev->hw_info.flash_spec.buffered) { /* Restoring protection causes the next read at a wrong location; * leave this out for now. */ REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_DONE); REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_WRDI | NVM_COMMAND_DOIT); /* Adjust timeout for emulation/FPGA */ cnt = NVRAM_TIMEOUT_COUNT; if (CHIP_REV(pdev) == CHIP_REV_FPGA) cnt *= 10; else if (CHIP_REV(pdev) == CHIP_REV_IKOS) cnt *= 100; lm_status = LM_STATUS_BUSY; for(j = 0; j < cnt; j++) { mm_wait(pdev, 5); REG_RD(pdev, nvm.nvm_command, &val); if(val & NVM_COMMAND_DONE) { lm_status = LM_STATUS_SUCCESS; break; } } } #endif return lm_status; } /* disable_nvram_write */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC lm_status_t enable_nvram_access( lm_device_t *pdev) { u32_t val; REG_RD(pdev, nvm.nvm_access_enable, &val); /* Enable both bits, even on read. */ REG_WR( pdev, nvm.nvm_access_enable, val | NVM_ACCESS_ENABLE_EN | NVM_ACCESS_ENABLE_WR_EN); return LM_STATUS_SUCCESS; } /* enable_nvram_access */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC lm_status_t disable_nvram_access( lm_device_t *pdev) { u32_t val; REG_RD(pdev, nvm.nvm_access_enable, &val); /* Disable both bits, even after read. */ REG_WR( pdev, nvm.nvm_access_enable, val & ~(NVM_ACCESS_ENABLE_EN | NVM_ACCESS_ENABLE_WR_EN)); return LM_STATUS_SUCCESS; } /* disable_nvram_access */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC lm_status_t nvram_erase_page( lm_device_t *pdev, u32_t offset) { lm_status_t lm_status; u32_t cmd_flags; u32_t val; u32_t j, cnt; if (pdev->hw_info.flash_spec.buffered) { /* Buffered flash, no erase needed */ return LM_STATUS_SUCCESS; } /* Build an erase command */ cmd_flags = NVM_COMMAND_ERASE | NVM_COMMAND_WR | NVM_COMMAND_DOIT; /* Need to clear DONE bit separately. */ REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_DONE); /* Address of the NVRAM to read from. */ REG_WR(pdev, nvm.nvm_addr, offset & NVM_ADDR_NVM_ADDR_VALUE); /* Issue an erase command. */ REG_WR(pdev, nvm.nvm_command, cmd_flags); /* Adjust timeout for emulation/FPGA */ cnt = NVRAM_TIMEOUT_COUNT; if (CHIP_REV(pdev) == CHIP_REV_FPGA) cnt *= 10; else if (CHIP_REV(pdev) == CHIP_REV_IKOS) cnt *= 100; /* Wait for completion. */ lm_status = LM_STATUS_BUSY; for(j = 0; j < cnt; j++) { mm_wait(pdev, 5); REG_RD(pdev, nvm.nvm_command, &val); if(val & NVM_COMMAND_DONE) { lm_status = LM_STATUS_SUCCESS; break; } } return lm_status; } /* nvram_erase_page */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC lm_status_t nvram_read_dword( lm_device_t *pdev, u32_t offset, u32_t *ret_val, u32_t nvram_flags) { lm_status_t lm_status; u32_t cmd_flags; u32_t val; u32_t j, cnt; /* Build the command word. */ cmd_flags = NVM_COMMAND_DOIT; if(nvram_flags & NVRAM_FLAG_SET_FIRST_CMD_BIT) { cmd_flags |= NVM_COMMAND_FIRST; } if(nvram_flags & NVRAM_FLAG_SET_LAST_CMD_BIT) { cmd_flags |= NVM_COMMAND_LAST; } if ((CHIP_NUM(pdev) == CHIP_NUM_5706) || (CHIP_NUM(pdev) == CHIP_NUM_5708)) { /* Calculate an offset of a buffered flash. */ if(nvram_flags & NVRAM_FLAG_BUFFERED_FLASH) { offset = ((offset / pdev->hw_info.flash_spec.page_size) << pdev->hw_info.flash_spec.shift_bits) + (offset % pdev->hw_info.flash_spec.page_size); } /* Need to clear DONE bit separately. */ REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_DONE); } /* Address of the NVRAM to read from. */ if (cmd_flags & NVM_COMMAND_FIRST) { REG_WR(pdev, nvm.nvm_addr, offset & NVM_ADDR_NVM_ADDR_VALUE); } /* Issue a read command. */ REG_WR(pdev, nvm.nvm_command, cmd_flags); /* Adjust timeout for emulation/FPGA */ cnt = NVRAM_TIMEOUT_COUNT; if (CHIP_REV(pdev) == CHIP_REV_FPGA) cnt *= 10; else if (CHIP_REV(pdev) == CHIP_REV_IKOS) cnt *= 100; /* Wait for completion. */ lm_status = LM_STATUS_BUSY; for(j = 0; j < cnt; j++) { mm_wait(pdev, 5); REG_RD(pdev, nvm.nvm_command, &val); if(val & NVM_COMMAND_DONE) { REG_RD(pdev, nvm.nvm_read, &val); /* Change to little endian. */ #if defined(LITTLE_ENDIAN) val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val & 0xff0000) >> 8) | ((val >> 24) & 0xff); #endif *ret_val = val; lm_status = LM_STATUS_SUCCESS; break; } } return lm_status; } /* nvram_read_dword */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC lm_status_t nvram_write_dword( lm_device_t *pdev, u32_t offset, u32_t val, u32_t nvram_flags) { lm_status_t lm_status; u32_t cmd_flags; u32_t j, cnt; /* Build the command word. */ cmd_flags = NVM_COMMAND_DOIT | NVM_COMMAND_WR; if(nvram_flags & NVRAM_FLAG_SET_FIRST_CMD_BIT) { cmd_flags |= NVM_COMMAND_FIRST; } if(nvram_flags & NVRAM_FLAG_SET_LAST_CMD_BIT) { cmd_flags |= NVM_COMMAND_LAST; } if ((CHIP_NUM(pdev) == CHIP_NUM_5706) || (CHIP_NUM(pdev) == CHIP_NUM_5708)) { /* Calculate an offset of a buffered flash. */ if(nvram_flags & NVRAM_FLAG_BUFFERED_FLASH) { offset = ((offset / pdev->hw_info.flash_spec.page_size) << pdev->hw_info.flash_spec.shift_bits) + (offset % pdev->hw_info.flash_spec.page_size); } /* Need to clear DONE bit separately. */ REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_DONE); } /* Change to little endian. */ #if defined(LITTLE_ENDIAN) val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val & 0xff0000) >> 8) | ((val >> 24) & 0xff); #endif /* Write the data. */ REG_WR(pdev, nvm.nvm_write, val); /* Address of the NVRAM to write to. */ if (cmd_flags & NVM_COMMAND_FIRST) { REG_WR(pdev, nvm.nvm_addr, offset & NVM_ADDR_NVM_ADDR_VALUE); } /* Issue the write command. */ REG_WR(pdev, nvm.nvm_command, cmd_flags); /* Adjust timeout for emulation/FPGA */ cnt = NVRAM_TIMEOUT_COUNT; if (CHIP_REV(pdev) == CHIP_REV_FPGA) cnt *= 10; else if (CHIP_REV(pdev) == CHIP_REV_IKOS) cnt *= 100; /* Wait for completion. */ lm_status = LM_STATUS_BUSY; for(j = 0; j < cnt; j++) { mm_wait(pdev, 5); REG_RD(pdev, nvm.nvm_command, &val); if(val & NVM_COMMAND_DONE) { lm_status = LM_STATUS_SUCCESS; break; } } return lm_status; } /* nvram_write_dword */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC u32_t find_atmel_size( lm_device_t *pdev) { u32_t orig, val, done=0, size=BUFFERED_FLASH_TOTAL_SIZE; if (CHIP_NUM(pdev) == CHIP_NUM_5709) { REG_RD(pdev, nvm.nvm_cfg4, &val); val &= 0x07; return (1 << val) ; } /* It is assumed that the flash is enabled and locked for exclusive access */ REG_RD(pdev, nvm.nvm_cfg3, &orig); REG_WR(pdev, nvm.nvm_cfg3, 0x57848353); REG_WR(pdev, nvm.nvm_read, 0); REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_DONE); REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_DOIT | NVM_COMMAND_FIRST | NVM_COMMAND_LAST); while (!done) { REG_RD(pdev, nvm.nvm_command, &val); if (val & NVM_COMMAND_DONE) { done = 1; } } REG_RD(pdev, nvm.nvm_read, &val); REG_WR(pdev, nvm.nvm_cfg3, orig); val &= 0x3c; switch (val) { case 0x24: size *= 8; break; case 0x1c: size *= 4; break; case 0x14: size *= 2; break; case 0x0c: size *= 1; break; default: size *= 0; break; } return size; } /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC u32_t find_stm_size( lm_device_t *pdev) { u32_t idx, val, result, bit; if (CHIP_NUM(pdev) == CHIP_NUM_5709) { REG_RD(pdev, nvm.nvm_cfg4, &val); val &= 0x07; return (1 << val) ; } /* It is assumed that the flash is enabled and locked for exclusive access */ /* Set CS, SO, SCLK as output, SI as input */ REG_WR(pdev, nvm.nvm_addr, NVM_ADDR_NVM_ADDR_VALUE_EECLK_TE | NVM_ADDR_NVM_ADDR_VALUE_EEDATA_TE | NVM_ADDR_NVM_ADDR_VALUE_SI_TE ); /* Set initial data CS=1, SO=0, SCLK=0, SI=n/a */ REG_WR(pdev, nvm.nvm_write, NVM_WRITE_NVM_WRITE_VALUE_EECLK_TE | NVM_WRITE_NVM_WRITE_VALUE_EEDATA_TE | NVM_WRITE_NVM_WRITE_VALUE_CS_B_TE ); /* Enable bit-bang mode */ REG_RD(pdev, nvm.nvm_cfg1, &val); REG_WR(pdev, nvm.nvm_cfg1, val | NVM_CFG1_BITBANG_MODE); mm_wait(pdev, 1); /* Bit-bang the command */ val = 0xf9; REG_WR(pdev, nvm.nvm_write, 0); mm_wait(pdev, 1); for (idx=0; idx < 8; idx++) { bit = ((val >> idx) & 0x1) << 4; REG_WR(pdev, nvm.nvm_write, bit); mm_wait(pdev, 1); REG_WR(pdev, nvm.nvm_write, NVM_WRITE_NVM_WRITE_VALUE_SCLK_TE | bit); mm_wait(pdev, 1); } REG_WR(pdev, nvm.nvm_write, 0); mm_wait(pdev, 1); /* Bit-bang to read ID, 1st byte: manuf ID; * 2nd byte: memory type; 3rd byte: memory capacity */ result = 0; for (idx = 0; idx < 24; idx++) { REG_RD(pdev, nvm.nvm_read, &val); bit = (val & NVM_WRITE_NVM_WRITE_VALUE_SI_TE) >> 5; result = (result << 1) | bit; REG_WR(pdev, nvm.nvm_write, NVM_WRITE_NVM_WRITE_VALUE_SCLK_TE); mm_wait(pdev, 1); REG_WR(pdev, nvm.nvm_write, 0); mm_wait(pdev, 1); } REG_WR(pdev, nvm.nvm_write, NVM_WRITE_NVM_WRITE_VALUE_CS_B_TE); mm_wait(pdev, 1); val = ST_MICRO_FLASH_1MBIT; switch (result) { case 0x00204014: val *= 8; break; case 0x00204013: val *= 4; break; case 0x00204012: val *= 2; break; case 0x00204011: val *= 1; break; default: val *= 0; break; } /* Get out of bit-bang mode */ REG_RD(pdev, nvm.nvm_cfg1, &idx); REG_WR(pdev, nvm.nvm_cfg1, idx & ~NVM_CFG1_BITBANG_MODE); mm_wait(pdev, 1); return val; } /******************************************************************************* * Description: * * Return: ******************************************************************************/ STATIC u32_t find_nvram_size( lm_device_t *pdev, u32_t table_idx) { lm_status_t lm_status; u32_t size, val; if (CHIP_NUM(pdev) == CHIP_NUM_5709) { REG_RD(pdev, nvm.nvm_cfg4, &val); val &= 0x07; return ((1 << val) * 1024 * 1024 / 8); } /* Request access to the flash interface. */ lm_status = acquire_nvram_lock(pdev); if(lm_status != LM_STATUS_SUCCESS) return 0; /* Enable access to flash interface */ lm_status = enable_nvram_access(pdev); if(lm_status != LM_STATUS_SUCCESS) return 0; switch (table_idx) { case 11: case 14: case 15: /* ATMEL */ size = find_atmel_size(pdev); break; case 5: case 6: size = find_stm_size(pdev); break; case 2: case 3: case 7: /* This one is static */ size = cfg_table[table_idx].total_size; break; default: size = 0; break; } /* Disable access to flash interface */ (void) disable_nvram_access(pdev); release_nvram_lock(pdev); return size; } /******************************************************************************* * Description: * * Return: ******************************************************************************/ void lm_nvram_init( lm_device_t *pdev, u8_t reset_flash_block) { u32_t idx, val; lm_status_t lm_status; DbgMessage(pdev, INFORM, "### lm_nvram_init\n"); if (CHIP_NUM(pdev) == CHIP_NUM_5709) { REG_RD(pdev, nvm.nvm_cfg4, &val); pdev->hw_info.flash_spec.buffered = 0; pdev->hw_info.flash_spec.shift_bits = 0; pdev->hw_info.flash_spec.page_size = SAIFUN_FLASH_PAGE_SIZE; pdev->hw_info.flash_spec.addr_mask = 0; pdev->hw_info.flash_spec.total_size = (1 << (val & 0x07)) * 1024 * 1024 / 8; return; } idx = lm_nvram_query(pdev, reset_flash_block, FALSE); if (idx == (u32_t)-1) { /* Not necessarily an error, it could mean that the flash block has * been reconfigured. */ return; } DbgMessage(pdev, INFORM, cfg_table[idx].name); DbgMessage(pdev, INFORM, " reconfiguring.\n"); /* Request access to the flash interface. */ lm_status = acquire_nvram_lock(pdev); if(lm_status != LM_STATUS_SUCCESS) return; /* Enable access to flash interface */ lm_status = enable_nvram_access(pdev); if(lm_status != LM_STATUS_SUCCESS) return; /* Reconfigure the flash interface */ /* Program the SPI and SEE clocks faster if FPGA or IKOS */ val = cfg_table[idx].config1; if(CHIP_REV(pdev) == CHIP_REV_FPGA) { val &= ~(NVM_CFG1_SPI_CLK_DIV | NVM_CFG1_SEE_CLK_DIV); val |= (0x0<<7) | (0x6<<11); } else if(CHIP_REV(pdev) == CHIP_REV_IKOS) { val &= ~(NVM_CFG1_SPI_CLK_DIV | NVM_CFG1_SEE_CLK_DIV); val |= (0x0<<7) | (0x0<<11); } else { /* No change, leave it */ } REG_WR(pdev, nvm.nvm_cfg1, val); REG_WR(pdev, nvm.nvm_cfg2, cfg_table[idx].config2); REG_WR(pdev, nvm.nvm_cfg3, cfg_table[idx].config3); REG_WR(pdev, nvm.nvm_write1, cfg_table[idx].write1); /* Disable access to flash interface */ (void) disable_nvram_access(pdev); release_nvram_lock(pdev); } /* lm_nvram_init */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ u32_t lm_nvram_query( lm_device_t *pdev, u8_t reset_flash_block, u8_t no_hw_mod) { u32_t val; u32_t j; u32_t cnt, idx, ret_val = (u32_t)-1; u8_t reconfigured = FALSE; u32_t entry_count, mask; DbgMessage(pdev, INFORM, "### lm_nvram_query\n"); if (CHIP_NUM(pdev) == CHIP_NUM_5709) { REG_RD(pdev, nvm.nvm_cfg4, &val); pdev->hw_info.flash_spec.buffered = 0; pdev->hw_info.flash_spec.shift_bits = 0; pdev->hw_info.flash_spec.page_size = SAIFUN_FLASH_PAGE_SIZE; pdev->hw_info.flash_spec.addr_mask = 0; pdev->hw_info.flash_spec.total_size = (1 << (val & 0x07)) * 1024 * 1024 / 8; return (u32_t)-1; } /* Adjust timeout for emulation/FPGA */ cnt = NVRAM_TIMEOUT_COUNT; if(CHIP_REV(pdev) == CHIP_REV_FPGA) cnt *= 10; else if(CHIP_REV(pdev) == CHIP_REV_IKOS) cnt *= 100; /* Reset the NVRAM interface block. */ if(reset_flash_block) { val = 0; /* Get access to write flash block register */ (void) enable_nvram_access(pdev); REG_WR(pdev, nvm.nvm_command, NVM_COMMAND_RST); for(j = 0; j < cnt; j++) { mm_wait(pdev, 5); REG_RD(pdev, nvm.nvm_command, &val); if(!(val & NVM_COMMAND_RST)) { break; } } DbgBreakIf(val & NVM_COMMAND_RST); } /* Determine the selected interface. */ REG_RD(pdev, nvm.nvm_cfg1, &val); entry_count = sizeof(cfg_table)/sizeof(new_nvm_cfg_t); if (val & (1<<30)) { /* Flash interface has been reconfigured */ mask = FLASH_BACKUP_STRAP_MASK; for (idx=0; idxhw_info.flash_spec.total_size = 0; DbgBreakMsg("Unsupported type.\n"); } else if (ret_val != (u32_t)-1) { /* Track what's been configured */ pdev->hw_info.flash_spec.buffered = cfg_table[ret_val].buffered; pdev->hw_info.flash_spec.shift_bits = cfg_table[ret_val].shift_bits; pdev->hw_info.flash_spec.page_size = cfg_table[ret_val].page_size; pdev->hw_info.flash_spec.addr_mask = cfg_table[ret_val].addr_mask; /* Determine the size before reconfiguring, dynamically */ if (no_hw_mod) { pdev->hw_info.flash_spec.total_size = cfg_table[ret_val].total_size; } else { pdev->hw_info.flash_spec.total_size = find_nvram_size(pdev, idx); } } else { pdev->hw_info.flash_spec.total_size = 0; DbgBreakMsg("Unknown flash/EEPROM type.\n"); } return (reconfigured) ? (u32_t)-1 : ret_val; } /* lm_nvram_query */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ lm_status_t lm_nvram_read( lm_device_t *pdev, u32_t offset, u32_t *ret_buf, u32_t buf_size) { lm_status_t lm_status; u32_t cmd_flags; DbgMessage(pdev, VERBOSE, "### lm_nvram_read\n"); if((buf_size & 0x03) || (offset & 0x03)) { DbgBreakMsg("Invalid paramter.\n"); return LM_STATUS_FAILURE; } if(offset + buf_size > pdev->hw_info.flash_spec.total_size) { DbgBreakMsg("Invalid paramter.\n"); return LM_STATUS_FAILURE; } if (pdev->hw_info.flash_spec.buffered) { cmd_flags = NVRAM_FLAG_BUFFERED_FLASH; } else { cmd_flags = NVRAM_FLAG_NONE; } /* Request access to the flash interface. */ lm_status = acquire_nvram_lock(pdev); if(lm_status != LM_STATUS_SUCCESS) { return lm_status; } /* Enable access to flash interface */ lm_status = enable_nvram_access(pdev); if(lm_status != LM_STATUS_SUCCESS) { return lm_status; } if(buf_size <= sizeof(u32_t)) { /* Address of the NVRAM to read from. */ cmd_flags |= NVRAM_FLAG_SET_FIRST_CMD_BIT | NVRAM_FLAG_SET_LAST_CMD_BIT; lm_status = nvram_read_dword(pdev, offset, ret_buf, cmd_flags); } else { /* Read the first word. */ cmd_flags |= NVRAM_FLAG_SET_FIRST_CMD_BIT; lm_status = nvram_read_dword(pdev, offset, ret_buf, cmd_flags); cmd_flags &= ~NVRAM_FLAG_SET_FIRST_CMD_BIT; if(lm_status == LM_STATUS_SUCCESS) { /* Advance to the next dword. */ offset += sizeof(u32_t); ret_buf++; buf_size -= sizeof(u32_t); while(buf_size > sizeof(u32_t) && lm_status == LM_STATUS_SUCCESS) { lm_status = nvram_read_dword(pdev, offset, ret_buf, cmd_flags); /* Advance to the next dword. */ offset += sizeof(u32_t); ret_buf++; buf_size -= sizeof(u32_t); } if(lm_status == LM_STATUS_SUCCESS) { cmd_flags |= NVRAM_FLAG_SET_LAST_CMD_BIT; lm_status = nvram_read_dword(pdev, offset, ret_buf, cmd_flags); } } } /* Disable access to flash interface */ (void) disable_nvram_access(pdev); release_nvram_lock(pdev); return lm_status; } /* lm_nvram_read */ /******************************************************************************* * Description: * * Return: ******************************************************************************/ lm_status_t lm_nvram_write( lm_device_t *pdev, u32_t offset, u32_t *data_buf, u32_t buf_size) { lm_status_t lm_status; u32_t cmd_flags; u32_t written_so_far, page_start, page_end, data_start, data_end; u32_t idx, *ptr32, addr, base_flags; static u32_t flash_buffer[66]; DbgMessage(pdev, VERBOSE, "### lm_nvram_write\n"); if(offset & 0x03) { DbgBreakMsg("Invalid paramter.\n"); return LM_STATUS_FAILURE; } if(offset + buf_size > pdev->hw_info.flash_spec.total_size) { DbgBreakMsg("Invalid paramter.\n"); return LM_STATUS_FAILURE; } lm_status = LM_STATUS_SUCCESS; written_so_far = 0; ptr32 = data_buf; if ((CHIP_NUM(pdev) == CHIP_NUM_5706) || (CHIP_NUM(pdev) == CHIP_NUM_5708)) { base_flags = (pdev->hw_info.flash_spec.buffered) ? NVRAM_FLAG_BUFFERED_FLASH : NVRAM_FLAG_NONE; while (written_so_far < buf_size) { /* Find the page_start addr */ page_start = offset + written_so_far; page_start -= (page_start % pdev->hw_info.flash_spec.page_size); /* Find the page_end addr */ page_end = page_start + pdev->hw_info.flash_spec.page_size; /* Find the data_start addr */ data_start = (written_so_far==0) ? offset : page_start; /* Find the data_end addr */ data_end = (page_end > offset + buf_size) ? (offset+buf_size) : page_end; /* Request access to the flash interface. */ lm_status = acquire_nvram_lock(pdev); if(lm_status != LM_STATUS_SUCCESS) return lm_status; /* Enable access to flash interface */ lm_status = enable_nvram_access(pdev); if(lm_status != LM_STATUS_SUCCESS) return lm_status; if (pdev->hw_info.flash_spec.buffered == 0) { /* Read the whole page into the buffer (non-buffer flash only) */ for (idx=0; idxhw_info.flash_spec.page_size; idx+=4) { cmd_flags = base_flags; if (idx==0) { cmd_flags |= NVRAM_FLAG_SET_FIRST_CMD_BIT; } if (idx==pdev->hw_info.flash_spec.page_size-4) { cmd_flags |= NVRAM_FLAG_SET_LAST_CMD_BIT; } lm_status |= nvram_read_dword(pdev, page_start+idx, &flash_buffer[idx/4], cmd_flags); } if(lm_status != LM_STATUS_SUCCESS) return lm_status; } /* Enable writes to flash interface (unlock write-protect) */ lm_status = enable_nvram_write(pdev); if(lm_status != LM_STATUS_SUCCESS) return lm_status; /* Erase the page */ lm_status = nvram_erase_page(pdev, page_start); if(lm_status != LM_STATUS_SUCCESS) return lm_status; /* Re-enable the write again for the actual write */ lm_status = enable_nvram_write(pdev); if(lm_status != LM_STATUS_SUCCESS) return lm_status; /* Loop to write back the buffer data from page_start to data_start */ cmd_flags = NVRAM_FLAG_SET_FIRST_CMD_BIT | base_flags; idx = 0; for (addr=page_start; addrhw_info.flash_spec.buffered == 0) { /* Write back only for non-buffered flash */ (void) nvram_write_dword(pdev, addr, flash_buffer[idx], cmd_flags); cmd_flags = base_flags; } } /* Loop to write the new data from data_start to data_end */ for (addr=data_start; addrhw_info.flash_spec.buffered) && (addr>=data_end-4))) { /* End of a page (page_end==data_end) * OR end of new data (in buffered flash case) */ cmd_flags |= NVRAM_FLAG_SET_LAST_CMD_BIT; } (void) nvram_write_dword(pdev, addr, *ptr32, cmd_flags); cmd_flags = base_flags; ptr32++; } /* Loop to write back the buffer data from data_end to page_end */ for (addr=data_end; addrhw_info.flash_spec.buffered == 0) { /* Write back only for non-buffered flash */ if (addr == page_end-4) { cmd_flags = NVRAM_FLAG_SET_LAST_CMD_BIT | base_flags; } (void) nvram_write_dword(pdev, addr, flash_buffer[idx], cmd_flags); cmd_flags = base_flags; } } /* Disable writes to flash interface (lock write-protect) */ (void) disable_nvram_write(pdev); /* Disable access to flash interface */ (void) disable_nvram_access(pdev); release_nvram_lock(pdev); /* Increment written_so_far */ written_so_far += data_end - data_start; } // while } else if (CHIP_NUM(pdev) == CHIP_NUM_5709) { /* Request access to the flash interface. */ lm_status = acquire_nvram_lock(pdev); if(lm_status != LM_STATUS_SUCCESS) return lm_status; /* Enable access to flash interface */ lm_status = enable_nvram_access(pdev); if(lm_status != LM_STATUS_SUCCESS) return lm_status; cmd_flags = NVRAM_FLAG_SET_FIRST_CMD_BIT; addr = offset; while (written_so_far < buf_size) { if (written_so_far == (buf_size - 4)) cmd_flags |= NVRAM_FLAG_SET_LAST_CMD_BIT; else if (((addr & 0xff) + 4) == 256) cmd_flags |= NVRAM_FLAG_SET_LAST_CMD_BIT; if ((addr & 0xff) == 0) cmd_flags |= NVRAM_FLAG_SET_FIRST_CMD_BIT; (void) nvram_write_dword(pdev, addr, *ptr32, cmd_flags); ptr32++; addr += 4; written_so_far += 4; cmd_flags = 0; } /* Disable access to flash interface */ (void) disable_nvram_access(pdev); release_nvram_lock(pdev); } return lm_status; } /* lm_nvram_write */