//
// 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 "../../../host/lib/usrp/n230/n230_fw_defs.h"
#include "../../../host/lib/usrp/n230/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);
}