diff options
Diffstat (limited to 'host/lib/usrp/x300/x300_mb_eeprom_iface.cpp')
-rw-r--r-- | host/lib/usrp/x300/x300_mb_eeprom_iface.cpp | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp b/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp new file mode 100644 index 000000000..adc766c39 --- /dev/null +++ b/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp @@ -0,0 +1,190 @@ +// +// Copyright 2013-2016 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 <http://www.gnu.org/licenses/>. +// + +/* + * x300_mb_eeprom_iface + * This interface was created to prevent MB EEPROM corruption while reading + * data during discovery. For devices with firmware version newer than 5.0, + * the EEPROM data is read into firmware memory and available without + * claiming the device. For devices with firmware versions 5.0 and older, + * the code makes sure to claim the device before driving the I2C bus. This + * has the unfortunate side effect of preventing multiple processes from + * discovering the device simultaneously, but is far better than having EEPROM + * corruption. + */ + +#include "x300_mb_eeprom_iface.hpp" +#include "x300_fw_common.h" +#include "x300_regs.hpp" +#include "x300_impl.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/platform.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/thread.hpp> + +using namespace uhd; + +static const uint32_t X300_FW_SHMEM_IDENT_MIN_VERSION = 0x50001; + +class x300_mb_eeprom_iface_impl : public x300_mb_eeprom_iface +{ +public: + + x300_mb_eeprom_iface_impl(wb_iface::sptr wb, i2c_iface::sptr i2c) : _wb(wb), _i2c(i2c) + { + _compat_num = _wb->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_COMPAT_NUM)); + } + + ~x300_mb_eeprom_iface_impl() + { + /* NOP */ + } + + /*! + * Write bytes over the i2c. + * \param addr the address + * \param buf the vector of bytes + */ + void write_i2c( + uint16_t addr, + const byte_vector_t &buf + ) + { + UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); + if (x300_impl::claim_status(_wb) != x300_impl::CLAIMED_BY_US) + { + throw uhd::io_error("Attempted to write MB EEPROM without claim to device."); + } + _i2c->write_i2c(addr, buf); + } + + /*! + * Read bytes over the i2c. + * \param addr the address + * \param num_bytes number of bytes to read + * \return a vector of bytes + */ + byte_vector_t read_i2c( + uint16_t addr, + size_t num_bytes + ) + { + UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); + byte_vector_t bytes; + if (_compat_num > X300_FW_SHMEM_IDENT_MIN_VERSION) + { + bytes = read_eeprom(addr, 0, num_bytes); + } else { + x300_impl::claim_status_t status = x300_impl::claim_status(_wb); + // Claim device before driving the I2C bus + if (status == x300_impl::CLAIMED_BY_US or x300_impl::try_to_claim(_wb)) + { + bytes = _i2c->read_i2c(addr, num_bytes); + if (status != x300_impl::CLAIMED_BY_US) + { + // We didn't originally have the claim, so give it up + x300_impl::release(_wb); + } + } + } + return bytes; + } + + /*! + * Write bytes to an eeprom. + * \param addr the address + * \param offset byte offset + * \param buf the vector of bytes + */ + void write_eeprom( + uint16_t addr, + uint16_t offset, + const byte_vector_t &buf + ) + { + UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); + if (x300_impl::claim_status(_wb) != x300_impl::CLAIMED_BY_US) + { + throw uhd::io_error("Attempted to write MB EEPROM without claim to device."); + } + _i2c->write_eeprom(addr, offset, buf); + } + + /*! + * Read bytes from an eeprom. + * \param addr the address + * \param offset byte offset + * \param num_bytes number of bytes to read + * \return a vector of bytes + */ + byte_vector_t read_eeprom( + uint16_t addr, + uint16_t offset, + size_t num_bytes + ) + { + UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); + byte_vector_t bytes; + x300_impl::claim_status_t status = x300_impl::claim_status(_wb); + if (_compat_num >= X300_FW_SHMEM_IDENT_MIN_VERSION) + { + // Get MB EEPROM data from firmware memory + if (num_bytes == 0) return bytes; + + size_t bytes_read = 0; + for (size_t word = offset / 4; bytes_read < num_bytes; word++) + { + uint32_t value = byteswap(_wb->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_IDENT + word))); + for (size_t byte = offset % 4; byte < 4 and bytes_read < num_bytes; byte++) + { + bytes.push_back(uint8_t((value >> (byte * 8)) & 0xff)); + bytes_read++; + } + } + } else { + // Claim device before driving the I2C bus + if (status == x300_impl::CLAIMED_BY_US or x300_impl::try_to_claim(_wb)) + { + bytes = _i2c->read_eeprom(addr, offset, num_bytes); + if (status != x300_impl::CLAIMED_BY_US) + { + // We didn't originally have the claim, so give it up + x300_impl::release(_wb); + } + } + } + return bytes; + } + + +private: + wb_iface::sptr _wb; + i2c_iface::sptr _i2c; + uint32_t _compat_num; +}; + +x300_mb_eeprom_iface::~x300_mb_eeprom_iface(void) +{ + /* NOP */ +} + +x300_mb_eeprom_iface::sptr x300_mb_eeprom_iface::make(wb_iface::sptr wb, i2c_iface::sptr i2c) +{ + return boost::make_shared<x300_mb_eeprom_iface_impl>(wb, i2c->eeprom16()); +} + |