1 //
2 // Copyright 2011,2014 Ettus Research LLC
3 // Copyright 2018 Ettus Research, a National Instruments Company
4 //
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 
8 #include <uhd/types/dict.hpp>
9 #include <uhd/utils/soft_register.hpp>
10 #include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
11 #include <unordered_map>
12 
13 using namespace uhd;
14 using namespace usrp;
15 
16 //-------------------------------------------------------------
17 // gpio_atr_3000
18 //-------------------------------------------------------------
19 
20 #define REG_ATR_IDLE_OFFSET (base + reg_offset * 0)
21 #define REG_ATR_RX_OFFSET (base + reg_offset * 1)
22 #define REG_ATR_TX_OFFSET (base + reg_offset * 2)
23 #define REG_ATR_FDX_OFFSET (base + reg_offset * 3)
24 #define REG_DDR_OFFSET (base + reg_offset * 4)
25 #define REG_ATR_DISABLE_OFFSET (base + reg_offset * 5)
26 
27 namespace {
28 // Special RB addr value to indicate no readback
29 // This value is invalid as a real address because it is not a multiple of 4
30 static constexpr wb_iface::wb_addr_type READBACK_DISABLED = 0xFFFFFFFF;
31 }; // namespace
32 
33 namespace uhd { namespace usrp { namespace gpio_atr {
34 
35 class gpio_atr_3000_impl : public gpio_atr_3000
36 {
37 public:
gpio_atr_3000_impl(wb_iface::sptr iface,const wb_iface::wb_addr_type base,const wb_iface::wb_addr_type rb_addr,const size_t reg_offset)38     gpio_atr_3000_impl(wb_iface::sptr iface,
39         const wb_iface::wb_addr_type base,
40         const wb_iface::wb_addr_type rb_addr,
41         const size_t reg_offset)
42         : _iface(iface)
43         , _rb_addr(rb_addr)
44         , _atr_idle_reg(REG_ATR_IDLE_OFFSET, _atr_disable_reg)
45         , _atr_rx_reg(REG_ATR_RX_OFFSET)
46         , _atr_tx_reg(REG_ATR_TX_OFFSET)
47         , _atr_fdx_reg(REG_ATR_FDX_OFFSET)
48         , _ddr_reg(REG_DDR_OFFSET)
49         , _atr_disable_reg(REG_ATR_DISABLE_OFFSET)
50     {
51         _atr_idle_reg.initialize(*_iface, true);
52         _atr_rx_reg.initialize(*_iface, true);
53         _atr_tx_reg.initialize(*_iface, true);
54         _atr_fdx_reg.initialize(*_iface, true);
55         _ddr_reg.initialize(*_iface, true);
56         _atr_disable_reg.initialize(*_iface, true);
57         for (const gpio_attr_map_t::value_type attr : gpio_attr_map) {
58             if (attr.first == usrp::gpio_atr::GPIO_SRC
59                 || attr.first == usrp::gpio_atr::GPIO_READBACK) {
60                 // Don't set the SRC and READBACK, they're handled elsewhere.
61                 continue;
62             }
63             _attr_reg_state.emplace(attr.first, 0);
64         }
65     }
66 
set_atr_mode(const gpio_atr_mode_t mode,const uint32_t mask)67     virtual void set_atr_mode(const gpio_atr_mode_t mode, const uint32_t mask)
68     {
69         // Each bit in the "ATR Disable" register determines whether the respective bit in
70         // the GPIO output bus is driven by the ATR engine or a static register. For each
71         // bit position, a 1 means that the bit is static and 0 means that the bit is
72         // driven by the ATR state machine. This setting will only get applied to all bits
73         // in the "mask" that are 1. All other bits will retain their old value.
74         auto value = (mode == MODE_ATR) ? ~MASK_SET_ALL : MASK_SET_ALL;
75         _atr_disable_reg.set_with_mask(value, mask);
76         _atr_disable_reg.flush();
77         // The attr state is inverted from _atr_disable_reg. In _atr_disable_reg,
78         // 1 == disable, whereas in CTRL, 1 means enable ATR.
79         _update_attr_state(GPIO_CTRL, ~value, mask);
80     }
81 
set_gpio_ddr(const gpio_ddr_t dir,const uint32_t mask)82     virtual void set_gpio_ddr(const gpio_ddr_t dir, const uint32_t mask)
83     {
84         // Each bit in the "DDR" register determines whether the respective bit in the
85         // GPIO bus is an input or an output. For each bit position, a 1 means that the
86         // bit is an output and 0 means that the bit is an input. This setting will only
87         // get applied to all bits in the "mask" that are 1. All other bits will retain
88         // their old value.
89         auto value = (dir == DDR_INPUT) ? ~MASK_SET_ALL : MASK_SET_ALL;
90         _ddr_reg.set_with_mask(value, mask);
91         _ddr_reg.flush();
92         _update_attr_state(GPIO_DDR, value, mask);
93     }
94 
set_atr_reg(const gpio_atr_reg_t atr,const uint32_t value,const uint32_t mask=MASK_SET_ALL)95     virtual void set_atr_reg(const gpio_atr_reg_t atr,
96         const uint32_t value,
97         const uint32_t mask = MASK_SET_ALL)
98     {
99         // Set the value of the specified ATR register. For bits with ATR Disable set to
100         // 1, the IDLE register will hold the output state This setting will only get
101         // applied to all bits in the "mask" that are 1. All other bits will retain their
102         // old value.
103         masked_reg_t* reg = NULL;
104         gpio_attr_t attr;
105         switch (atr) {
106             case ATR_REG_IDLE:
107                 reg  = &_atr_idle_reg;
108                 attr = GPIO_ATR_0X;
109 
110                 break;
111             case ATR_REG_RX_ONLY:
112                 reg  = &_atr_rx_reg;
113                 attr = GPIO_ATR_RX;
114 
115                 break;
116             case ATR_REG_TX_ONLY:
117                 reg  = &_atr_tx_reg;
118                 attr = GPIO_ATR_TX;
119 
120                 break;
121             case ATR_REG_FULL_DUPLEX:
122                 reg  = &_atr_fdx_reg;
123                 attr = GPIO_ATR_XX;
124 
125                 break;
126             default:
127                 reg  = &_atr_idle_reg;
128                 attr = GPIO_ATR_0X;
129                 break;
130         }
131         // For protection we only write to bits that have the mode ATR by masking the user
132         // specified "mask" with ~atr_disable.
133         reg->set_with_mask(value, mask);
134         reg->flush();
135         _update_attr_state(attr, value, mask);
136     }
137 
set_gpio_out(const uint32_t value,const uint32_t mask=MASK_SET_ALL)138     virtual void set_gpio_out(const uint32_t value, const uint32_t mask = MASK_SET_ALL)
139     {
140         // Set the value of the specified GPIO output register.
141         // This setting will only get applied to all bits in the "mask" that are 1. All
142         // other bits will retain their old value.
143 
144         // For protection we only write to bits that have the mode GPIO by masking the
145         // user specified "mask" with atr_disable.
146         _atr_idle_reg.set_gpio_out_with_mask(value, mask);
147         _atr_idle_reg.flush();
148         _update_attr_state(GPIO_OUT, value, mask);
149     }
150 
read_gpio()151     virtual uint32_t read_gpio()
152     {
153         // Read the state of the GPIO pins
154         // If a pin is configured as an input, reads the actual value of the pin
155         // If a pin is configured as an output, reads the last value written to the pin
156         if (_rb_addr != READBACK_DISABLED) {
157             return _iface->peek32(_rb_addr);
158         } else {
159             throw uhd::runtime_error("read_gpio not supported for write-only interface.");
160         }
161     }
162 
get_attr_reg(const gpio_attr_t attr)163     virtual uint32_t get_attr_reg(const gpio_attr_t attr)
164     {
165         if (attr == GPIO_SRC) {
166             throw uhd::runtime_error("Can't get GPIO source by GPIO ATR interface.");
167         }
168         if (attr == GPIO_READBACK) {
169             return read_gpio();
170         }
171         if (!_attr_reg_state.count(attr)) {
172             throw uhd::runtime_error("Invalid GPIO attr!");
173         }
174         // Return the cached value of the requested attr
175         return _attr_reg_state.at(attr);
176     }
177 
set_gpio_attr(const gpio_attr_t attr,const uint32_t value)178     inline virtual void set_gpio_attr(const gpio_attr_t attr, const uint32_t value)
179     {
180         // An attribute based API to configure all settings for the GPIO bus in one
181         // function call. This API does not have a mask so it configures all bits at the
182         // same time.
183         switch (attr) {
184             case GPIO_SRC:
185                 throw uhd::runtime_error("Can't set GPIO source by GPIO ATR interface.");
186             case GPIO_CTRL:
187                 set_atr_mode(
188                     MODE_ATR, value); // Configure mode=ATR for all bits that are set
189                 set_atr_mode(
190                     MODE_GPIO, ~value); // Configure mode=GPIO for all bits that are unset
191                 break;
192             case GPIO_DDR:
193                 set_gpio_ddr(
194                     DDR_OUTPUT, value); // Configure as output for all bits that are set
195                 set_gpio_ddr(
196                     DDR_INPUT, ~value); // Configure as input for all bits that are unset
197                 break;
198             case GPIO_OUT:
199                 // Only set bits that are driven statically
200                 set_gpio_out(value);
201                 break;
202             case GPIO_ATR_0X:
203                 // Only set bits that are driven by the ATR engine
204                 set_atr_reg(ATR_REG_IDLE, value);
205                 break;
206             case GPIO_ATR_RX:
207                 // Only set bits that are driven by the ATR engine
208                 set_atr_reg(ATR_REG_RX_ONLY, value);
209                 break;
210             case GPIO_ATR_TX:
211                 // Only set bits that are driven by the ATR engine
212                 set_atr_reg(ATR_REG_TX_ONLY, value);
213                 break;
214             case GPIO_ATR_XX:
215                 // Only set bits that are driven by the ATR engine
216                 set_atr_reg(ATR_REG_FULL_DUPLEX, value);
217                 break;
218             case GPIO_READBACK:
219                 // This is readonly register, ignore on set.
220                 break;
221             default:
222                 UHD_THROW_INVALID_CODE_PATH();
223         }
224     }
225 
226 protected:
227     class masked_reg_t : public uhd::soft_reg32_wo_t
228     {
229     public:
masked_reg_t(const wb_iface::wb_addr_type offset)230         masked_reg_t(const wb_iface::wb_addr_type offset) : uhd::soft_reg32_wo_t(offset)
231         {
232             uhd::soft_reg32_wo_t::set(REGISTER, 0);
233         }
234 
set_with_mask(const uint32_t value,const uint32_t mask)235         virtual void set_with_mask(const uint32_t value, const uint32_t mask)
236         {
237             uhd::soft_reg32_wo_t::set(REGISTER,
238                 (value & mask) | (uhd::soft_reg32_wo_t::get(REGISTER) & (~mask)));
239         }
240 
get()241         virtual uint32_t get()
242         {
243             return uhd::soft_reg32_wo_t::get(uhd::soft_reg32_wo_t::REGISTER);
244         }
245 
flush()246         virtual void flush()
247         {
248             uhd::soft_reg32_wo_t::flush();
249         }
250     };
251 
252     class atr_idle_reg_t : public masked_reg_t
253     {
254     public:
atr_idle_reg_t(const wb_iface::wb_addr_type offset,masked_reg_t & atr_disable_reg)255         atr_idle_reg_t(const wb_iface::wb_addr_type offset, masked_reg_t& atr_disable_reg)
256             : masked_reg_t(offset)
257             , _atr_idle_cache(0)
258             , _gpio_out_cache(0)
259             , _atr_disable_reg(atr_disable_reg)
260         {
261         }
262 
set_with_mask(const uint32_t value,const uint32_t mask)263         virtual void set_with_mask(const uint32_t value, const uint32_t mask)
264         {
265             _atr_idle_cache = (value & mask) | (_atr_idle_cache & (~mask));
266         }
267 
get()268         virtual uint32_t get()
269         {
270             return _atr_idle_cache;
271         }
272 
set_gpio_out_with_mask(const uint32_t value,const uint32_t mask)273         void set_gpio_out_with_mask(const uint32_t value, const uint32_t mask)
274         {
275             _gpio_out_cache = (value & mask) | (_gpio_out_cache & (~mask));
276         }
277 
get_gpio_out()278         virtual uint32_t get_gpio_out()
279         {
280             return _gpio_out_cache;
281         }
282 
flush()283         virtual void flush()
284         {
285             set(REGISTER,
286                 (_atr_idle_cache & (~_atr_disable_reg.get()))
287                     | (_gpio_out_cache & _atr_disable_reg.get()));
288             masked_reg_t::flush();
289         }
290 
291     private:
292         uint32_t _atr_idle_cache;
293         uint32_t _gpio_out_cache;
294         masked_reg_t& _atr_disable_reg;
295     };
296 
297     std::unordered_map<gpio_attr_t, uint32_t, std::hash<size_t>> _attr_reg_state;
298     wb_iface::sptr _iface;
299     wb_iface::wb_addr_type _rb_addr;
300     atr_idle_reg_t _atr_idle_reg;
301     masked_reg_t _atr_rx_reg;
302     masked_reg_t _atr_tx_reg;
303     masked_reg_t _atr_fdx_reg;
304     masked_reg_t _ddr_reg;
305     masked_reg_t _atr_disable_reg;
306 
_update_attr_state(const gpio_attr_t attr,const uint32_t val,const uint32_t mask)307     void _update_attr_state(
308         const gpio_attr_t attr, const uint32_t val, const uint32_t mask)
309     {
310         _attr_reg_state[attr] = (_attr_reg_state.at(attr) & ~mask) | (val & mask);
311     }
312 };
313 
make(wb_iface::sptr iface,const wb_iface::wb_addr_type base,const wb_iface::wb_addr_type rb_addr,const size_t reg_offset)314 gpio_atr_3000::sptr gpio_atr_3000::make(wb_iface::sptr iface,
315     const wb_iface::wb_addr_type base,
316     const wb_iface::wb_addr_type rb_addr,
317     const size_t reg_offset)
318 {
319     return sptr(new gpio_atr_3000_impl(iface, base, rb_addr, reg_offset));
320 }
321 
make_write_only(wb_iface::sptr iface,const wb_iface::wb_addr_type base,const size_t reg_offset)322 gpio_atr_3000::sptr gpio_atr_3000::make_write_only(
323     wb_iface::sptr iface, const wb_iface::wb_addr_type base, const size_t reg_offset)
324 {
325     gpio_atr_3000::sptr gpio_iface(
326         new gpio_atr_3000_impl(iface, base, READBACK_DISABLED, reg_offset));
327     gpio_iface->set_gpio_ddr(DDR_OUTPUT, MASK_SET_ALL);
328     return gpio_iface;
329 }
330 
331 //-------------------------------------------------------------
332 // db_gpio_atr_3000
333 //-------------------------------------------------------------
334 
335 class db_gpio_atr_3000_impl : public gpio_atr_3000_impl, public db_gpio_atr_3000
336 {
337 public:
db_gpio_atr_3000_impl(wb_iface::sptr iface,const wb_iface::wb_addr_type base,const wb_iface::wb_addr_type rb_addr,const size_t reg_offset)338     db_gpio_atr_3000_impl(wb_iface::sptr iface,
339         const wb_iface::wb_addr_type base,
340         const wb_iface::wb_addr_type rb_addr,
341         const size_t reg_offset)
342         : gpio_atr_3000_impl(iface, base, rb_addr, reg_offset)
343     { /* NOP */
344     }
345 
set_pin_ctrl(const db_unit_t unit,const uint32_t value,const uint32_t mask)346     inline void set_pin_ctrl(
347         const db_unit_t unit, const uint32_t value, const uint32_t mask)
348     {
349         gpio_atr_3000_impl::set_atr_mode(MODE_ATR, compute_mask(unit, value & mask));
350         gpio_atr_3000_impl::set_atr_mode(MODE_GPIO, compute_mask(unit, (~value) & mask));
351     }
352 
get_pin_ctrl(const db_unit_t unit)353     inline uint32_t get_pin_ctrl(const db_unit_t unit)
354     {
355         return (~_atr_disable_reg.get()) >> compute_shift(unit);
356     }
357 
358     using gpio_atr_3000_impl::set_gpio_ddr;
set_gpio_ddr(const db_unit_t unit,const uint32_t value,const uint32_t mask)359     inline void set_gpio_ddr(
360         const db_unit_t unit, const uint32_t value, const uint32_t mask)
361     {
362         gpio_atr_3000_impl::set_gpio_ddr(DDR_OUTPUT, compute_mask(unit, value & mask));
363         gpio_atr_3000_impl::set_gpio_ddr(DDR_INPUT, compute_mask(unit, (~value) & mask));
364     }
365 
get_gpio_ddr(const db_unit_t unit)366     inline uint32_t get_gpio_ddr(const db_unit_t unit)
367     {
368         return _ddr_reg.get() >> compute_shift(unit);
369     }
370 
371     using gpio_atr_3000_impl::set_atr_reg;
set_atr_reg(const db_unit_t unit,const gpio_atr_reg_t atr,const uint32_t value,const uint32_t mask)372     inline void set_atr_reg(const db_unit_t unit,
373         const gpio_atr_reg_t atr,
374         const uint32_t value,
375         const uint32_t mask)
376     {
377         gpio_atr_3000_impl::set_atr_reg(
378             atr, value << compute_shift(unit), compute_mask(unit, mask));
379     }
380 
get_atr_reg(const db_unit_t unit,const gpio_atr_reg_t atr)381     inline uint32_t get_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr)
382     {
383         masked_reg_t* reg = NULL;
384         switch (atr) {
385             case ATR_REG_IDLE:
386                 reg = &_atr_idle_reg;
387                 break;
388             case ATR_REG_RX_ONLY:
389                 reg = &_atr_rx_reg;
390                 break;
391             case ATR_REG_TX_ONLY:
392                 reg = &_atr_tx_reg;
393                 break;
394             case ATR_REG_FULL_DUPLEX:
395                 reg = &_atr_fdx_reg;
396                 break;
397             default:
398                 reg = &_atr_idle_reg;
399                 break;
400         }
401         return (reg->get() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit);
402     }
403 
404     using gpio_atr_3000_impl::set_gpio_out;
set_gpio_out(const db_unit_t unit,const uint32_t value,const uint32_t mask)405     inline void set_gpio_out(
406         const db_unit_t unit, const uint32_t value, const uint32_t mask)
407     {
408         gpio_atr_3000_impl::set_gpio_out(
409             static_cast<uint32_t>(value) << compute_shift(unit),
410             compute_mask(unit, mask));
411     }
412 
get_gpio_out(const db_unit_t unit)413     inline uint32_t get_gpio_out(const db_unit_t unit)
414     {
415         return (_atr_idle_reg.get_gpio_out() & compute_mask(unit, MASK_SET_ALL))
416                >> compute_shift(unit);
417     }
418 
419     using gpio_atr_3000_impl::read_gpio;
read_gpio(const db_unit_t unit)420     inline uint32_t read_gpio(const db_unit_t unit)
421     {
422         return (gpio_atr_3000_impl::read_gpio() & compute_mask(unit, MASK_SET_ALL))
423                >> compute_shift(unit);
424     }
425 
426 private:
compute_shift(const db_unit_t unit)427     inline uint32_t compute_shift(const db_unit_t unit)
428     {
429         switch (unit) {
430             case dboard_iface::UNIT_RX:
431                 return 0;
432             case dboard_iface::UNIT_TX:
433                 return 16;
434             default:
435                 return 0;
436         }
437     }
438 
compute_mask(const db_unit_t unit,const uint32_t mask)439     inline uint32_t compute_mask(const db_unit_t unit, const uint32_t mask)
440     {
441         uint32_t tmp_mask = (unit == dboard_iface::UNIT_BOTH) ? mask : (mask & 0xFFFF);
442         return tmp_mask << (compute_shift(unit));
443     }
444 };
445 
make(wb_iface::sptr iface,const wb_iface::wb_addr_type base,const wb_iface::wb_addr_type rb_addr,const size_t reg_offset)446 db_gpio_atr_3000::sptr db_gpio_atr_3000::make(wb_iface::sptr iface,
447     const wb_iface::wb_addr_type base,
448     const wb_iface::wb_addr_type rb_addr,
449     const size_t reg_offset)
450 {
451     return sptr(new db_gpio_atr_3000_impl(iface, base, rb_addr, reg_offset));
452 }
453 
454 }}} // namespace uhd::usrp::gpio_atr
455