aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/cores/spi_core_3000.cpp
blob: 2abbac31783120f72766f5fecd5aadef0e2b7f6a (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
//
// Copyright 2013-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 "spi_core_3000.hpp"
#include <uhd/exception.hpp>
#include <uhd/utils/log.hpp>
#include <boost/thread/thread.hpp> //sleep

#define SPI_DIV      _base + 0
#define SPI_CTRL     _base + 4
#define SPI_DATA     _base + 8
#define SPI_SHUTDOWN _base + 12

using namespace uhd;

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

class spi_core_3000_impl : public spi_core_3000
{
public:
    spi_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback):
        _iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0), _divider_cache(0)
    {
        this->set_divider(30);
    }

    uint32_t transact_spi(
        int which_slave,
        const spi_config_t &config,
        uint32_t data,
        size_t num_bits,
        bool readback
    ){
        boost::lock_guard<boost::mutex> lock(_mutex);

        //load SPI divider
        size_t spi_divider = _div;
        if (config.use_custom_divider) {
            //The resulting SPI frequency will be f_system/(2*(divider+1))
            //This math ensures the frequency will be equal to or less than the target
            spi_divider = (config.divider-1)/2;
        }

        //conditionally send SPI divider
        if (spi_divider != _divider_cache) {
            _iface->poke32(SPI_DIV, spi_divider);
            _divider_cache = spi_divider;
        }

        //load control word
        uint32_t ctrl_word = 0;
        ctrl_word |= ((which_slave & 0xffffff) << 0);
        ctrl_word |= ((num_bits & 0x3f) << 24);
        if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31);
        if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30);

        //conditionally send control word
        if (_ctrl_word_cache != ctrl_word)
        {
            _iface->poke32(SPI_CTRL, ctrl_word);
            _ctrl_word_cache = ctrl_word;
        }

        //load data word (must be in upper bits)
        const uint32_t data_out = data << (32 - num_bits);

        //send data word
        _iface->poke32(SPI_DATA, data_out);

        //conditional readback
        if (readback)
        {
            return _iface->peek32(_readback);
        }

        return 0;
    }

    void set_shutdown(const bool shutdown)
    {
        _shutdown_cache = shutdown;
        _iface->poke32(SPI_SHUTDOWN, _shutdown_cache);
    }

    bool get_shutdown()
    {
        return(_shutdown_cache);
    }

    void set_divider(const double div)
    {
        _div = size_t((div/2) - 0.5);
    }

private:

    wb_iface::sptr _iface;
    const size_t _base;
    const size_t _readback;
    uint32_t _ctrl_word_cache;
    bool _shutdown_cache;
    boost::mutex _mutex;
    size_t _div;
    size_t _divider_cache;
};

spi_core_3000::sptr spi_core_3000::make(wb_iface::sptr iface, const size_t base, const size_t readback)
{
    return sptr(new spi_core_3000_impl(iface, base, readback));
}