aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/b200/b200_mb_eeprom.cpp
blob: 5a37cc9c11471f57d92652a5f8596c94d3373a5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//
// 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);
}

}

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);
    }
}