aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/octoclock/lib/clkdist.c
blob: 0468ac38efba20ce7f037b7c924d81775ed88786 (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
/*
 * Copyright 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 <avr/io.h>

#include <octoclock.h>
#include <clkdist.h>
#include <state.h>

#include <util/delay.h>

#define wait() for(uint16_t u=14000; u; u--) asm("nop");

#define CLK   (PA0) // Shift by 0 bits
#define CE_   (PA1) // Is really the "Chip Disable" signal, as Hi disables SPI
#define MOSI  (PA2)
#define MISO  (PA3)
#define PD_   (PA4)
#define SYNC_ (PA5)

// Table of 32-bit constants to be written to the TI chip's registers. These are
// from the "Special Settings" on Page 35 of the datasheet.
// For the GPS's 10 MHz output
static const uint32_t table_Pri_Ref[] = {
    Bits_32(1,01010100,0,0),    // Reg 0
    Bits_32(1,01010100,0,0),    // Outputs LVCMOS Positive&Negative Active - Non-inverted
    Bits_32(1,01010100,0,0),
    Bits_32(1,01010100,0,0),
    Bits_32(1,01010100,0,0),    // All have output divide ratio to be 1; Aux Output is OFF
    Bits_32(0,0,1001,11010100), // Reg 5  LVCMOS in; p31 of TI datasheet
    Bits_32(1,0,0010000,0),     // Reg 6    // SCAS863A <96> NOVEMBER 2008 <96> REVISED JUNE 2011
    Bits_32(1,01000000,0,0),    // Reg 7
    Bits_32(0,0,1,10000000)     // Reg8  Status/Control
};

// For the External 10 MHz input LVDS with external termination, 
// Effectively DC coupled
static const uint32_t table_Sec_Ref[] = {
    Bits_32(0001,01010100,0,100000),    // Reg 0 -- use Secondary Reference for all channels
    Bits_32(0001,01010100,0,100000),    // Outputs LVCMOS Positive&Negative Active - Non-inverted
    Bits_32(0001,01010100,0,100000),
    Bits_32(0001,01010100,0,100000),
    Bits_32(0001,01010100,0,100000),
    Bits_32(0,0,1,10011011),            // Reg 5, Failsafe OFF   b5.11 = 0
    Bits_32(1,0,10000,0),               // Reg 6; try again
    Bits_32(1,01000000,0,0),
    Bits_32(0,0,1,10000000)             // Reg8  Status/Control
};

// Table 19 conflicts with Tables 5 thru 9 - in how LVCMOS outputs are defined
// extra error in Table 9, for bits 24 and 25
static int table_size = sizeof (table_Pri_Ref) / sizeof(uint32_t);

static void set_bit(uint8_t  bit_number, Levels bit_value) {

    if(bit_value == Hi)
        PORTA |= 1<<bit_number;
    else
        PORTA &= ~ (1<<bit_number);
}

static bool get_bit(uint8_t  bit_number) {
    asm("nop");

    uint8_t portA = PINA;
    return (portA &  1<< bit_number) > 0 ? true : false;
}

// Send 32 bits to TI chip, LSB first.
// Don't worry about reading any bits back at this time
static void send_SPI(uint32_t bits) {
    // Basically, when the clock is low, one can set MOSI to anything, as it's
    // ignored.
    set_bit(CE_, Lo);    // Start SPI transaction with TI chip

    // Send each bit, LSB first, add a bit of delay before the clock, and then
    // toggle the clock line.
    for (uint8_t i=0; i<32; i++) {
        set_bit(MOSI, ((bits & (1UL<<i)) ? Hi : Lo) );
        asm("nop");
        set_bit(CLK, Hi);
        set_bit(CLK, Lo);
    }

    // OK, transaction is over
    set_bit(CE_, Hi);
}

static uint32_t receive_SPI() {
    uint32_t bits = 0;

    set_bit(CE_, Hi); // Make sure we're inactive
    set_bit(CLK, Lo); // and clk line is inactive, too
    set_bit(MOSI,Lo); // Make our bit output zero, for good measure
    set_bit(CE_, Lo); // Start SPI transaction with TI chip; MOSI is don't care

    // For each bit we are receiving, prep, clock in the bit LSB first
    for (uint8_t i=0; i<32; i++){
        bits >>= 1;
        set_bit(CLK, Hi);
        if( get_bit(MISO) ) bits |= 0x80000000;
        set_bit(CLK, Lo);
    }

    // OK, transaction is over
    set_bit(CE_, Hi);

    // Ditch the lower 4 bits, which only contain the address
    return (uint32_t)(bits >> 4);
}

void setup_TI_CDCE18005(TI_Input_10_MHz which_input) {
    // Send the table of data to init the clock distribution chip.  Uses SPI.
    uint32_t temp;

    if(which_input == Primary_GPS) {
        for(uint8_t i=0; i<table_size; i++){
            temp = table_Pri_Ref[i]<<4;
            temp |= i;
            // Make sure the register's address is in the LSBs
            send_SPI(temp);
        }
    } else {
        // is Secondary_Ext -- External 10 MHz input from SMA connector
        for(uint8_t i=0; i<table_size; i++){
            temp = table_Sec_Ref[i]<<4;
            temp |= i;
            // Make sure the register's address is in the LSBs
            send_SPI(temp);
         }
    }
}

void reset_TI_CDCE18005(void) {
    // First, reset the chip.  Or, if you will, pull /SYNC low then high
    set_bit(CE_, Hi);
    set_bit(PD_, Lo);
    wait();

    // Out of Power Down state
    set_bit(PD_, Hi);
    wait();

    set_bit(SYNC_, Lo);
    wait();
    set_bit(SYNC_, Hi);

    wait();
}

uint32_t get_TI_CDCE18005(CDCE18005 which_register){
    uint32_t get_reg_value = 0;
    get_reg_value = (0xf0 & (which_register << 4)) | Read_Command;

    // This tells the TI chip to send us the reg. value requested
    send_SPI(get_reg_value);
    return receive_SPI();
}

void set_TI_CDCE18005(CDCE18005 which_register, uint32_t bits){
    send_SPI((bits << 4) | which_register);
}

bool check_TI_CDCE18005(TI_Input_10_MHz which_input,
        CDCE18005 which_register) {

    if(which_input == Primary_GPS){
        uint32_t read_value = get_TI_CDCE18005(which_register);
        return read_value == table_Pri_Ref[which_register];
    } else {
        uint32_t read_value = get_TI_CDCE18005(which_register);
        return read_value == table_Sec_Ref[which_register];
    }
}