aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/cores/gpio_core_200.cpp
blob: 3bce8e078d52ee02eb94c3810b36c5baa3d59848 (plain)
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//
// Copyright 2011,2014 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include "gpio_core_200.hpp"
#include <uhd/types/dict.hpp>

#define REG_GPIO_IDLE          _base + 0
#define REG_GPIO_RX_ONLY       _base + 4
#define REG_GPIO_TX_ONLY       _base + 8
#define REG_GPIO_BOTH          _base + 12
#define REG_GPIO_DDR           _base + 16

using namespace uhd;
using namespace usrp;

template <typename T>
static void shadow_it(T &shadow, const T &value, const T &mask){
    shadow = (shadow & ~mask) | (value & mask);
}

gpio_core_200::~gpio_core_200(void){
    /* NOP */
}

class gpio_core_200_impl : public gpio_core_200{
public:
    gpio_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t rb_addr):
        _iface(iface), _base(base), _rb_addr(rb_addr), _first_atr(true) { /* NOP */ }

    void set_pin_ctrl(const unit_t unit, const uint16_t value, const uint16_t mask){
        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
        shadow_it(_pin_ctrl[unit], value, mask);
        update(); //full update
    }

    uint16_t get_pin_ctrl(unit_t unit){
        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
        return _pin_ctrl[unit];
    }

    void set_atr_reg(const unit_t unit, const atr_reg_t atr, const uint16_t value, const uint16_t mask){
        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
        shadow_it(_atr_regs[unit][atr], value, mask);
        if (_first_atr)
        {
            // To preserve legacy behavior, update all registers the first time
            update();
            _first_atr = false;
        }
        else
            update(atr);
    }

    uint16_t get_atr_reg(unit_t unit, atr_reg_t reg){
        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
        return _atr_regs[unit][reg];
    }

    void set_gpio_ddr(const unit_t unit, const uint16_t value, const uint16_t mask){
        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
        shadow_it(_gpio_ddr[unit], value, mask);
        _iface->poke32(REG_GPIO_DDR, //update the 32 bit register
            (uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) |
            (uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX))
        );
    }

    uint16_t get_gpio_ddr(unit_t unit){
        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
        return _gpio_ddr[unit];
    }

    void set_gpio_out(const unit_t unit, const uint16_t value, const uint16_t mask){
        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
        shadow_it(_gpio_out[unit], value, mask);
        this->update(); //full update
    }

    uint16_t get_gpio_out(unit_t unit){
        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
        return _gpio_out[unit];
    }

    uint16_t read_gpio(const unit_t unit){
        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
        return uint16_t(_iface->peek32(_rb_addr) >> shift_by_unit(unit));
    }

private:
    wb_iface::sptr _iface;
    const size_t _base;
    const size_t _rb_addr;
    bool _first_atr;
    uhd::dict<size_t, uint32_t> _update_cache;

    uhd::dict<unit_t, uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr;
    uhd::dict<unit_t, uhd::dict<atr_reg_t, uint16_t> > _atr_regs;

    unsigned shift_by_unit(const unit_t unit){
        return (unit == dboard_iface::UNIT_RX)? 0 : 16;
    }

    void update(void){
        update(gpio_atr::ATR_REG_IDLE);
        update(gpio_atr::ATR_REG_TX_ONLY);
        update(gpio_atr::ATR_REG_RX_ONLY);
        update(gpio_atr::ATR_REG_FULL_DUPLEX);
    }

    void update(const atr_reg_t atr){
        size_t addr;
        switch (atr)
        {
        case gpio_atr::ATR_REG_IDLE:
            addr = REG_GPIO_IDLE;
            break;
        case gpio_atr::ATR_REG_TX_ONLY:
            addr = REG_GPIO_TX_ONLY;
            break;
        case gpio_atr::ATR_REG_RX_ONLY:
            addr = REG_GPIO_RX_ONLY;
            break;
        case gpio_atr::ATR_REG_FULL_DUPLEX:
            addr = REG_GPIO_BOTH;
            break;
        default:
            UHD_THROW_INVALID_CODE_PATH();
        }
        const uint32_t atr_val =
            (uint32_t(_atr_regs[dboard_iface::UNIT_RX][atr]) << shift_by_unit(dboard_iface::UNIT_RX)) |
            (uint32_t(_atr_regs[dboard_iface::UNIT_TX][atr]) << shift_by_unit(dboard_iface::UNIT_TX));

        const uint32_t gpio_val =
            (uint32_t(_gpio_out[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) |
            (uint32_t(_gpio_out[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX));

        const uint32_t ctrl =
            (uint32_t(_pin_ctrl[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) |
            (uint32_t(_pin_ctrl[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX));
        const uint32_t val = (ctrl & atr_val) | ((~ctrl) & gpio_val);
        if (not _update_cache.has_key(addr) or _update_cache[addr] != val)
        {
            _iface->poke32(addr, val);
        }
        _update_cache[addr] = val;
    }

};

gpio_core_200::sptr gpio_core_200::make(wb_iface::sptr iface, const size_t base, const size_t rb_addr){
    return sptr(new gpio_core_200_impl(iface, base, rb_addr));
}

gpio_core_200_32wo::~gpio_core_200_32wo(void){
    /* NOP */
}

class gpio_core_200_32wo_impl : public gpio_core_200_32wo{
public:
    gpio_core_200_32wo_impl(wb_iface::sptr iface, const size_t base):
        _iface(iface), _base(base)
    {
        set_ddr_reg();
    }

    void set_ddr_reg(){
        _iface->poke32(REG_GPIO_DDR, 0xffffffff);
    }


    void set_atr_reg(const atr_reg_t atr, const uint32_t value){
        if (atr == gpio_atr::ATR_REG_IDLE)
            _iface->poke32(REG_GPIO_IDLE, value);
        else if (atr == gpio_atr::ATR_REG_TX_ONLY)
            _iface->poke32(REG_GPIO_TX_ONLY, value);
        else if (atr == gpio_atr::ATR_REG_RX_ONLY)
            _iface->poke32(REG_GPIO_RX_ONLY, value);
        else if (atr == gpio_atr::ATR_REG_FULL_DUPLEX)
            _iface->poke32(REG_GPIO_BOTH, value);
        else
            UHD_THROW_INVALID_CODE_PATH();
    }

    void set_all_regs(const uint32_t value){
        set_atr_reg(gpio_atr::ATR_REG_IDLE,        value);
        set_atr_reg(gpio_atr::ATR_REG_TX_ONLY,     value);
        set_atr_reg(gpio_atr::ATR_REG_RX_ONLY,     value);
        set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, value);
    }

private:
    wb_iface::sptr _iface;
    const size_t _base;

};

gpio_core_200_32wo::sptr gpio_core_200_32wo::make(wb_iface::sptr iface, const size_t base){
    return sptr(new gpio_core_200_32wo_impl(iface, base));
}