mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
Note: template_lvbitx.{cpp,hpp} need to be excluded from the list of
files that clang-format gets applied against.
157 lines
5 KiB
C++
157 lines
5 KiB
C++
//
|
|
// Copyright 2017-2019 Ettus Research, a National Instruments Brand
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
|
|
#include "b200_impl.hpp"
|
|
#include <uhd/usrp/mboard_eeprom.hpp>
|
|
#include <uhdlib/utils/eeprom_utils.hpp>
|
|
#include <unordered_map>
|
|
|
|
using namespace uhd;
|
|
using uhd::usrp::mboard_eeprom_t;
|
|
|
|
namespace {
|
|
|
|
constexpr auto LOG_ID = "B2xx_EEPROM";
|
|
|
|
struct eeprom_field_t
|
|
{
|
|
size_t offset;
|
|
size_t length;
|
|
};
|
|
|
|
// EEPROM map information is duplicated in common_const.h for the
|
|
// firmware and bootloader code.
|
|
// EEPROM map information is duplicated in b2xx_fx3_utils.cpp
|
|
|
|
constexpr uint16_t SIGNATURE_ADDR = 0x0000;
|
|
constexpr size_t SIGNATURE_LENGTH = 4;
|
|
|
|
constexpr auto EXPECTED_MAGIC = "45568"; // 0xB200
|
|
constexpr auto EXPECTED_COMPAT = "1";
|
|
|
|
constexpr uint32_t REV1_SIGNATURE = 0xB01A5943;
|
|
constexpr uint16_t REV1_BASE_ADDR = 0x7F00;
|
|
constexpr size_t REV1_LENGTH = 46;
|
|
|
|
const std::unordered_map<std::string, eeprom_field_t> B200_REV1_MAP = {
|
|
{"magic", {0, 2}},
|
|
{"eeprom_revision", {2, 2}},
|
|
{"eeprom_compat", {4, 2}},
|
|
{"vendor_id", {6, 2}},
|
|
{"product_id", {8, 2}},
|
|
{"revision", {10, 2}},
|
|
{"product", {12, 2}},
|
|
{"name", {14, NAME_MAX_LEN}},
|
|
{"serial", {14 + NAME_MAX_LEN, SERIAL_LEN}},
|
|
// pad of 210 bytes
|
|
};
|
|
|
|
constexpr uint32_t REV0_SIGNATURE = 0xB2145943;
|
|
constexpr uint16_t REV0_BASE_ADDR = 0x04DC;
|
|
constexpr size_t REV0_LENGTH = 36;
|
|
|
|
const std::unordered_map<std::string, eeprom_field_t> B200_REV0_MAP = {
|
|
// front pad of 220 bytes
|
|
{"revision", {0, 2}},
|
|
{"product", {2, 2}},
|
|
{"name", {4, NAME_MAX_LEN}},
|
|
{"serial", {4 + NAME_MAX_LEN, SERIAL_LEN}},
|
|
};
|
|
|
|
constexpr int UNKNOWN_REV = -1;
|
|
|
|
int _get_rev(uhd::i2c_iface::sptr iface)
|
|
{
|
|
auto bytes =
|
|
iface->read_eeprom(SIGNATURE_ADDR >> 8, SIGNATURE_ADDR & 0xFF, SIGNATURE_LENGTH);
|
|
uint32_t signature = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
|
|
if (signature == REV0_SIGNATURE) {
|
|
return 0;
|
|
} else if (signature == REV1_SIGNATURE) {
|
|
return 1;
|
|
} else {
|
|
UHD_LOG_WARNING(LOG_ID, "Unknown signature! 0x" << std::hex << signature);
|
|
return UNKNOWN_REV;
|
|
}
|
|
}
|
|
|
|
byte_vector_t _get_eeprom(uhd::i2c_iface::sptr iface)
|
|
{
|
|
const auto rev = _get_rev(iface);
|
|
if (rev == UNKNOWN_REV) {
|
|
return byte_vector_t();
|
|
}
|
|
|
|
const uint16_t addr = (rev == 0) ? REV0_BASE_ADDR : REV1_BASE_ADDR;
|
|
const size_t length = (rev == 0) ? REV0_LENGTH : REV1_LENGTH;
|
|
|
|
return iface->read_eeprom(addr >> 8, addr & 0xFF, length);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
mboard_eeprom_t b200_impl::get_mb_eeprom(uhd::i2c_iface::sptr iface)
|
|
{
|
|
auto rev = _get_rev(iface);
|
|
auto bytes = _get_eeprom(iface);
|
|
mboard_eeprom_t mb_eeprom;
|
|
if (rev == UNKNOWN_REV or bytes.empty()) {
|
|
return mb_eeprom;
|
|
}
|
|
|
|
auto eeprom_map = (rev == 0) ? B200_REV0_MAP : B200_REV1_MAP;
|
|
|
|
for (const auto& element : eeprom_map) {
|
|
// There is an assumption here that fields of length 2 are uint16's and
|
|
// lengths other than 2 are strings. Update this code if that
|
|
// assumption changes.
|
|
byte_vector_t element_bytes = byte_vector_t(bytes.begin() + element.second.offset,
|
|
bytes.begin() + element.second.offset + element.second.length);
|
|
|
|
mb_eeprom[element.first] = (element.second.length == 2)
|
|
? uint16_bytes_to_string(element_bytes)
|
|
: bytes_to_string(element_bytes);
|
|
}
|
|
|
|
if (rev > 0) {
|
|
if (mb_eeprom["magic"] != EXPECTED_MAGIC) {
|
|
throw uhd::runtime_error(
|
|
str(boost::format("EEPROM magic value mismatch. Device returns %s, but "
|
|
"should have been %s.")
|
|
% mb_eeprom["magic"] % EXPECTED_MAGIC));
|
|
}
|
|
if (mb_eeprom["eeprom_compat"] != EXPECTED_COMPAT) {
|
|
throw uhd::runtime_error(
|
|
str(boost::format("EEPROM compat value mismatch. Device returns %s, but "
|
|
"should have been %s.")
|
|
% mb_eeprom["eeprom_compat"] % EXPECTED_COMPAT));
|
|
}
|
|
}
|
|
|
|
return mb_eeprom;
|
|
}
|
|
|
|
void b200_impl::set_mb_eeprom(const mboard_eeprom_t& mb_eeprom)
|
|
{
|
|
const auto rev = _get_rev(_iface);
|
|
auto eeprom_map = (rev == 0) ? B200_REV0_MAP : B200_REV1_MAP;
|
|
auto base_addr = (rev == 0) ? REV0_BASE_ADDR : REV1_BASE_ADDR;
|
|
for (const auto& key : mb_eeprom.keys()) {
|
|
if (eeprom_map.find(key) == eeprom_map.end()) {
|
|
UHD_LOG_WARNING(
|
|
LOG_ID, "Unknown key in mb_eeprom during set_mb_eeprom: " << key);
|
|
continue;
|
|
}
|
|
|
|
// There is an assumption here that fields of length 2 are uint16's and
|
|
// lengths other than 2 are strings. Update this code if that
|
|
// assumption changes.
|
|
auto field = eeprom_map.at(key);
|
|
auto bytes = (field.length == 2) ? string_to_uint16_bytes(mb_eeprom[key])
|
|
: string_to_bytes(mb_eeprom[key], field.length);
|
|
_iface->write_eeprom(base_addr >> 8, (base_addr & 0xFF) + field.offset, bytes);
|
|
}
|
|
}
|