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