1 //
2 // Copyright 2013-2014 Ettus Research LLC
3 // Copyright 2018 Ettus Research, a National Instruments Company
4 // Copyright 2019 Ettus Research, a National Instruments Brand
5 //
6 // SPDX-License-Identifier: GPL-3.0-or-later
7 //
8
9 #include <uhd/exception.hpp>
10 #include <uhdlib/usrp/cores/spi_core_3000.hpp>
11 #include <chrono>
12 #include <memory>
13 #include <mutex>
14 #include <thread>
15
16 namespace {
17 constexpr double DEFAULT_DIVIDER = 30.0;
18 }
19
20 using namespace uhd;
21
~spi_core_3000(void)22 spi_core_3000::~spi_core_3000(void)
23 {
24 /* NOP */
25 }
26
27 class spi_core_3000_impl : public spi_core_3000
28 {
29 public:
spi_core_3000_impl(poke32_fn_t && poke32_fn,peek32_fn_t && peek32_fn,const size_t base,const size_t reg_offset,const size_t readback)30 spi_core_3000_impl(poke32_fn_t&& poke32_fn,
31 peek32_fn_t&& peek32_fn,
32 const size_t base,
33 const size_t reg_offset,
34 const size_t readback)
35 : _poke32(std::move(poke32_fn))
36 , _peek32(std::move(peek32_fn))
37 , _spi_div_addr(base + 0 * reg_offset)
38 , _spi_ctrl_addr(base + 1 * reg_offset)
39 , _spi_data_addr(base + 2 * reg_offset)
40 , _readback_addr(readback)
41 {
42 this->set_divider(DEFAULT_DIVIDER);
43 }
44
transact_spi(int which_slave,const spi_config_t & config,uint32_t data,size_t num_bits,bool readback)45 uint32_t transact_spi(int which_slave,
46 const spi_config_t& config,
47 uint32_t data,
48 size_t num_bits,
49 bool readback)
50 {
51 std::lock_guard<std::mutex> lock(_mutex);
52
53 // load SPI divider
54 size_t spi_divider = _div;
55 if (config.use_custom_divider) {
56 // The resulting SPI frequency will be f_system/(2*(divider+1))
57 // This math ensures the frequency will be equal to or less than the target
58 spi_divider = (config.divider - 1) / 2;
59 }
60
61 // conditionally send SPI divider
62 if (spi_divider != _divider_cache) {
63 _poke32(_spi_div_addr, spi_divider);
64 _divider_cache = spi_divider;
65 }
66
67 // load control word
68 uint32_t ctrl_word = 0;
69 ctrl_word |= ((which_slave & 0xffffff) << 0);
70 ctrl_word |= ((num_bits & 0x3f) << 24);
71 if (config.mosi_edge == spi_config_t::EDGE_FALL)
72 ctrl_word |= (1 << 31);
73 if (config.miso_edge == spi_config_t::EDGE_RISE)
74 ctrl_word |= (1 << 30);
75
76 // conditionally send control word
77 if (_ctrl_word_cache != ctrl_word) {
78 _poke32(_spi_ctrl_addr, ctrl_word);
79 _ctrl_word_cache = ctrl_word;
80 }
81
82 // load data word (must be in upper bits)
83 const uint32_t data_out = data << (32 - num_bits);
84
85 // send data word
86 _poke32(_spi_data_addr, data_out);
87
88 // conditional readback
89 if (readback) {
90 return _peek32(_readback_addr);
91 }
92
93 return 0;
94 }
95
set_divider(const double div)96 void set_divider(const double div)
97 {
98 _div = size_t((div / 2) - 0.5);
99 }
100
101 private:
102 poke32_fn_t _poke32;
103 peek32_fn_t _peek32;
104 const size_t _spi_div_addr;
105 const size_t _spi_ctrl_addr;
106 const size_t _spi_data_addr;
107 const size_t _readback_addr;
108 uint32_t _ctrl_word_cache = 0;
109 std::mutex _mutex;
110 size_t _div;
111 size_t _divider_cache = 0;
112 };
113
make(wb_iface::sptr iface,const size_t base,const size_t readback)114 spi_core_3000::sptr spi_core_3000::make(
115 wb_iface::sptr iface, const size_t base, const size_t readback)
116 {
117 return std::make_shared<spi_core_3000_impl>(
118 [iface](
119 const uint32_t addr, const uint32_t value) { iface->poke32(addr, value); },
120 [iface](const uint32_t addr) { return iface->peek32(addr); },
121 base,
122 4,
123 readback);
124 }
125
make(spi_core_3000::poke32_fn_t && poke32_fn,spi_core_3000::peek32_fn_t && peek32_fn,const size_t base,const size_t reg_offset,const size_t readback)126 spi_core_3000::sptr spi_core_3000::make(spi_core_3000::poke32_fn_t&& poke32_fn,
127 spi_core_3000::peek32_fn_t&& peek32_fn,
128 const size_t base,
129 const size_t reg_offset,
130 const size_t readback)
131 {
132 return std::make_shared<spi_core_3000_impl>(
133 std::move(poke32_fn), std::move(peek32_fn), base, reg_offset, readback);
134 }
135