// // Copyright 2014 Ettus Research LLC // // This program 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 3 of the License, or // (at your option) any later version. // // This program 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 this program. If not, see . // #include "n230_eeprom.h" #include #include #include #include #include //memcpy #include "n230_fw_defs.h" #include "n230_fw_host_iface.h" static const wb_spi_slave_t flash_spi_slave = { .base = (void*) 0xB000, .slave_sel = 0x0001, .clk_div = 4, //80MHz/4 = 20MHz .mosi_edge = RISING, .miso_edge = FALLING, .lsb_first = false }; static const spi_flash_dev_t spi_flash_device = { .page_size = 256, .sector_size = 65536, .num_sectors = 254, .bus = &flash_spi_slave }; /*********************************************************************** * Non-volatile device data **********************************************************************/ #define N230_FLASH_NV_DATA_OFFSET 0x800000 //Default values in case the EEPROM is not read, corrupt const n230_eeprom_map_t default_eeprom = { .data_version_major = N230_EEPROM_VER_MAJOR, .data_version_minor = N230_EEPROM_VER_MINOR, .hw_revision = 0, .hw_product = 0x01, .gateway = N230_DEFAULT_GATEWAY, .eth_info = { { //eth0 .mac_addr = N230_DEFAULT_ETH0_MAC, .subnet = N230_DEFAULT_ETH0_MASK, .ip_addr = N230_DEFAULT_ETH0_IP }, { //eth1 .mac_addr = N230_DEFAULT_ETH1_MAC, .subnet = N230_DEFAULT_ETH1_MASK, .ip_addr = N230_DEFAULT_ETH1_IP } } }; //EEPROM cache static spi_flash_session_t flash_session = {.device = NULL}; static n230_eeprom_map_t eeprom_cache; static bool cache_dirty = true; bool read_n230_eeprom() { bool status = false; if (flash_session.device == NULL) { //Initialize flash session structure for the first time wb_spi_init(spi_flash_device.bus); spif_init(&flash_session, &spi_flash_device, spif_spsn_s25flxx_operations()); } spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t)); //Verify data format status = (eeprom_cache.data_version_major == default_eeprom.data_version_major); //Sanity communication info if (eeprom_cache.eth_info[0].ip_addr == 0xFFFFFFFF) eeprom_cache.eth_info[0].ip_addr = default_eeprom.eth_info[0].ip_addr; if (eeprom_cache.eth_info[1].ip_addr == 0xFFFFFFFF) eeprom_cache.eth_info[1].ip_addr = default_eeprom.eth_info[1].ip_addr; if (eeprom_cache.eth_info[0].subnet == 0xFFFFFFFF) eeprom_cache.eth_info[0].subnet = default_eeprom.eth_info[0].subnet; if (eeprom_cache.eth_info[1].subnet == 0xFFFFFFFF) eeprom_cache.eth_info[1].subnet = default_eeprom.eth_info[1].subnet; if (!status) { UHD_FW_TRACE(WARN, "read_n230_eeprom: Initialized cache to the default map."); memcpy(&eeprom_cache, &default_eeprom, sizeof(n230_eeprom_map_t)); } cache_dirty = !status; return status; } bool write_n230_eeprom() { //Assumption: sizeof(n230_eeprom_map_t) <= flash_page_size //This function would need to be reimplemented if this assumption is no longer true if (sizeof(n230_eeprom_map_t) > flash_session.device->page_size) { UHD_FW_TRACE(ERROR, "write_n230_eeprom: sizeof(n230_eeprom_map_t) > flash_page_size"); return false; } bool status = true; if (cache_dirty) { n230_eeprom_map_t device_eeprom; spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &device_eeprom, sizeof(n230_eeprom_map_t)); if (memcmp(&eeprom_cache, &device_eeprom, sizeof(n230_eeprom_map_t)) != 0) { //Cache does not match read state. Write. UHD_FW_TRACE(DEBUG, "write_n230_eeprom: Writing data to flash..."); status = spif_erase_sector_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET); if (status) { status = spif_write_page_sync( &flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t)); } if (!status) { UHD_FW_TRACE(ERROR, "write_n230_eeprom: Operation failed!"); } cache_dirty = !status; } else { UHD_FW_TRACE(DEBUG, "write_n230_eeprom: No new data. Write skipped."); //Cache matches read state. So mark as clean cache_dirty = false; } } return status; } bool is_n230_eeprom_cache_dirty() { return cache_dirty; } n230_eeprom_map_t* get_n230_eeprom_map() { cache_dirty = true; return &eeprom_cache; } const n230_eeprom_map_t* get_n230_const_eeprom_map() { return &eeprom_cache; } const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface) { if (iface >= N230_NUM_ETH_PORTS) { UHD_FW_TRACE_FSTR(ERROR, "get_n230_ethernet_info called with iface=%d when there are only %d ports!!!", iface, N230_NUM_ETH_PORTS); } return &(get_n230_const_eeprom_map()->eth_info[iface]); } /*********************************************************************** * Storage for bootstrap FPGA Image **********************************************************************/ #define N230_FLASH_FPGA_IMAGE_OFFSET 0x000000 #define N230_FLASH_FPGA_IMAGE_SIZE 0x400000 #define N230_FLASH_NUM_FPGA_IMAGES 2 void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes) { if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) { UHD_FW_TRACE_FSTR(ERROR, "read_n230_fpga_image_page: Offset 0x%x out of bounds", offset); } spif_read_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes); } bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes) { if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) { UHD_FW_TRACE_FSTR(ERROR, "write_n230_fpga_image_page: Offset 0x%x out of bounds", offset); return false; } return spif_write_page_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes); } bool erase_n230_fpga_image_sector(uint32_t offset) { if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) { UHD_FW_TRACE_FSTR(ERROR, "erase_n230_fpga_image_sector: Offset 0x%x out of bounds", offset); return false; } return spif_erase_sector_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset); }