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
|
//
// Copyright 2011-2012,2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include <uhd/exception.hpp>
#include <uhd/utils/log.hpp>
#include <uhdlib/usrp/cores/i2c_core_200.hpp>
#include <chrono>
#include <mutex>
#include <thread>
#define REG_I2C_WR_PRESCALER_LO (1 << 3) | 0
#define REG_I2C_WR_PRESCALER_HI (1 << 3) | 1
#define REG_I2C_WR_CTRL (1 << 3) | 2
#define REG_I2C_WR_DATA (1 << 3) | 3
#define REG_I2C_WR_CMD (1 << 3) | 4
#define REG_I2C_RD_DATA (0 << 3) | 3
#define REG_I2C_RD_ST (0 << 3) | 4
//
// STA, STO, RD, WR, and IACK bits are cleared automatically
//
#define I2C_CTRL_EN (1 << 7) // core enable
#define I2C_CTRL_IE (1 << 6) // interrupt enable
#define I2C_CMD_START (1 << 7) // generate (repeated) start condition
#define I2C_CMD_STOP (1 << 6) // generate stop condition
#define I2C_CMD_RD (1 << 5) // read from slave
#define I2C_CMD_WR (1 << 4) // write to slave
#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1)
#define I2C_CMD_RSVD_2 (1 << 2) // reserved
#define I2C_CMD_RSVD_1 (1 << 1) // reserved
#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt
#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK)
#define I2C_ST_BUSY \
(1 << 6) // 1 after START signal detected; 0 after STOP signal detected
#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration
#define I2C_ST_RSVD_4 (1 << 4) // reserved
#define I2C_ST_RSVD_3 (1 << 3) // reserved
#define I2C_ST_RSVD_2 (1 << 2) // reserved
#define I2C_ST_TIP (1 << 1) // Transfer-in-progress
#define I2C_ST_IP (1 << 0) // Interrupt pending
using namespace uhd;
i2c_core_200::~i2c_core_200(void)
{
/* NOP */
}
class i2c_core_200_impl : public i2c_core_200
{
public:
i2c_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t readback)
: _iface(iface), _base(base), _readback(readback)
{
// init I2C FPGA interface.
this->poke(REG_I2C_WR_CTRL, 0x0000);
// set prescalers to operate at 400kHz: WB_CLK is 64MHz...
static const uint32_t i2c_datarate = 400000;
static const uint32_t wishbone_clk = 64000000; // FIXME should go somewhere else
uint16_t prescaler = wishbone_clk / (i2c_datarate * 5) - 1;
this->poke(REG_I2C_WR_PRESCALER_LO, prescaler & 0xFF);
this->poke(REG_I2C_WR_PRESCALER_HI, (prescaler >> 8) & 0xFF);
this->poke(REG_I2C_WR_CTRL, I2C_CTRL_EN); // enable I2C core
}
void write_i2c(uint16_t addr, const byte_vector_t& bytes) override
{
this->poke(REG_I2C_WR_DATA, (addr << 1) | 0); // addr and read bit (0)
this->poke(REG_I2C_WR_CMD,
I2C_CMD_WR | I2C_CMD_START | (bytes.empty() ? I2C_CMD_STOP : 0));
// wait for previous transfer to complete
if (not wait_chk_ack()) {
this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP);
return;
}
for (size_t i = 0; i < bytes.size(); i++) {
this->poke(REG_I2C_WR_DATA, bytes[i]);
this->poke(REG_I2C_WR_CMD,
I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0));
if (!wait_chk_ack()) {
this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP);
return;
}
}
}
byte_vector_t read_i2c(uint16_t addr, size_t num_bytes) override
{
byte_vector_t bytes;
if (num_bytes == 0)
return bytes;
while (this->peek(REG_I2C_RD_ST) & I2C_ST_BUSY) {
/* NOP */
}
this->poke(REG_I2C_WR_DATA, (addr << 1) | 1); // addr and read bit (1)
this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | I2C_CMD_START);
// wait for previous transfer to complete
if (not wait_chk_ack()) {
this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP);
}
for (size_t i = 0; i < num_bytes; i++) {
this->poke(REG_I2C_WR_CMD,
I2C_CMD_RD | ((num_bytes == i + 1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0));
i2c_wait();
bytes.push_back(this->peek(REG_I2C_RD_DATA));
}
return bytes;
}
private:
void i2c_wait(void)
{
for (size_t i = 0; i < 100; i++) {
if ((this->peek(REG_I2C_RD_ST) & I2C_ST_TIP) == 0)
return;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
UHD_LOGGER_ERROR("CORES") << "i2c_core_200: i2c_wait timeout";
}
bool wait_chk_ack(void)
{
i2c_wait();
return (this->peek(REG_I2C_RD_ST) & I2C_ST_RXACK) == 0;
}
void poke(const size_t what, const uint8_t cmd)
{
std::lock_guard<std::mutex> lock(_mutex);
_iface->poke32(_base, (what << 8) | cmd);
}
uint8_t peek(const size_t what)
{
std::lock_guard<std::mutex> lock(_mutex);
_iface->poke32(_base, what << 8);
return uint8_t(_iface->peek32(_readback));
}
wb_iface::sptr _iface;
const size_t _base;
const size_t _readback;
std::mutex _mutex;
};
i2c_core_200::sptr i2c_core_200::make(
wb_iface::sptr iface, const size_t base, const size_t readback)
{
return sptr(new i2c_core_200_impl(iface, base, readback));
}
|