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