Imported Upstream version 3.2.2
[debian/gnuradio] / usrp2 / firmware / lib / clocks.c
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2008 Free Software Foundation, Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 #include <clocks.h>
23
24 #include "memory_map.h"
25 #include "ad9510.h"
26 #include "spi.h"
27 #include "u2_init.h"
28 #include "nonstdio.h"
29
30 void 
31 clocks_init(void)
32 {
33   // Set up basic clocking functions in AD9510
34   ad9510_write_reg(0x45, 0x00); // CLK2 drives distribution
35
36   clocks_enable_fpga_clk(true, 1);
37
38   spi_wait();
39
40   // Set up PLL for 10 MHz reference
41   // Reg 4, A counter, Don't Care
42   ad9510_write_reg(0x05, 0x00);  // Reg 5, B counter MSBs, 0
43   ad9510_write_reg(0x06, 0x05);  // Reg 6, B counter LSBs, 5
44   // Reg 7, Loss of reference detect, doesn't work yet, 0
45   ad9510_write_reg(0x5A, 0x01); // Update Regs
46
47
48   // FIXME, probably need interface to this...
49   timesync_regs->tick_control = 4;
50
51   // Primary clock configuration
52   clocks_mimo_config(MC_WE_DONT_LOCK);
53
54   // Set up other clocks
55   clocks_enable_test_clk(false, 0);
56   clocks_enable_tx_dboard(false, 0);
57   clocks_enable_rx_dboard(false, 0);
58   clocks_enable_eth_phyclk(false, 0);
59
60   // Enable clock to ADCs and DACs
61   clocks_enable_dac_clk(true, 1);
62   clocks_enable_adc_clk(true, 1);
63 }
64
65
66 void
67 clocks_mimo_config(int flags)
68 {
69   if (flags & _MC_WE_LOCK){
70     // Reg 8, Charge pump on, dig lock det, positive PFD, 47
71     ad9510_write_reg(0x08, 0x47);
72   }
73   else {
74     // Reg 8, Charge pump off, dig lock det, positive PFD
75     ad9510_write_reg(0x08, 0x00);
76   }
77   
78   // Reg 9, Charge pump current, 0x40=3mA, 0x00=650uA
79   ad9510_write_reg(0x09, 0x00);
80   // Reg A, Prescaler of 2, everything normal 04
81   ad9510_write_reg(0x0A, 0x04);
82   // Reg B, R Div MSBs, 0
83   ad9510_write_reg(0x0B, 0x00);
84   // Reg C, R Div LSBs, 1
85   ad9510_write_reg(0x0C, 0x01);
86   // Reg D, Antibacklash, Digital lock det, 0
87
88   ad9510_write_reg(0x5A, 0x01); // Update Regs
89
90   spi_wait();
91   
92   // Allow for clock switchover
93   
94   if (flags & _MC_WE_LOCK){             // WE LOCK
95     if (flags & _MC_MIMO_CLK_INPUT) {
96       // Turn on ref output and choose the MIMO connector
97       output_regs->clk_ctrl = 0x15;  
98     }
99     else {
100       // turn on ref output and choose the SMA
101       output_regs->clk_ctrl = 0x1C; 
102     }
103   }
104   else {                                // WE DONT LOCK
105     // Disable both ext clk inputs
106     output_regs->clk_ctrl = 0x10;
107   }
108
109   // Do we drive a clock onto the MIMO connector?
110   if (flags & MC_PROVIDE_CLK_TO_MIMO)
111     clocks_enable_clkexp_out(true,10);
112   else
113     clocks_enable_clkexp_out(false,0); 
114 }
115
116 bool 
117 clocks_lock_detect()
118 {
119   if(pic_regs->pending & PIC_CLKSTATUS)
120     return true;
121   return false;
122 }
123
124 int inline
125 clocks_gen_div(int divisor)
126 {
127   int L,H;
128   L = (divisor>>1)-1;
129   H = divisor-L-2;
130   return (L<<4)|H;
131 }
132
133 #define CLOCK_OUT_EN 0x08
134 #define CLOCK_OUT_DIS_CMOS 0x01
135 #define CLOCK_OUT_DIS_PECL 0x02
136 #define CLOCK_DIV_DIS 0x80
137 #define CLOCK_DIV_EN 0x00
138
139 #define CLOCK_MODE_PECL 1
140 #define CLOCK_MODE_LVDS 2
141 #define CLOCK_MODE_CMOS 3
142
143 void 
144 clocks_enable_XXX_clk(bool enable, int divisor, int reg_en, int reg_div, int mode)
145 {
146   int enable_word, div_word, div_en_word;
147
148   switch(mode) {
149   case CLOCK_MODE_PECL :
150     enable_word = enable ? 0x08 : 0x0A;
151     break;
152   case CLOCK_MODE_LVDS :
153     enable_word = enable ? 0x02 : 0x03;
154     break;
155   case CLOCK_MODE_CMOS :
156     enable_word = enable ? 0x08 : 0x09;
157     break;
158   }
159   if(enable && (divisor>1)) {
160     div_word = clocks_gen_div(divisor);
161     div_en_word = CLOCK_DIV_EN;
162   }
163   else {
164     div_word = 0;
165     div_en_word = CLOCK_DIV_DIS;
166   }
167
168   ad9510_write_reg(reg_en,enable_word); // Output en/dis
169   ad9510_write_reg(reg_div,div_word); // Set divisor
170   ad9510_write_reg(reg_div+1,div_en_word); // Enable or Bypass Divider
171   ad9510_write_reg(0x5A, 0x01);  // Update Regs
172 }
173
174 // Clock 0
175 void
176 clocks_enable_test_clk(bool enable, int divisor)
177 {
178   clocks_enable_XXX_clk(enable,divisor,0x3C,0x48,CLOCK_MODE_PECL);
179 }
180
181 // Clock 1
182 void
183 clocks_enable_fpga_clk(bool enable, int divisor)
184 {
185   clocks_enable_XXX_clk(enable,divisor,0x3D,0x4A,CLOCK_MODE_PECL);
186 }
187
188 // Clock 2 on Rev 3, Clock 5 on Rev 4
189 void
190 clocks_enable_clkexp_out(bool enable, int divisor)
191 {
192   if(u2_hw_rev_major == 3)
193     clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL);
194   else if(u2_hw_rev_major == 4) {
195     ad9510_write_reg(0x34,0x00);  // Turn on fine delay
196     ad9510_write_reg(0x35,0x00);  // Set Full Scale to nearly 10ns
197     ad9510_write_reg(0x36,0x1c);  // Set fine delay.  0x20 is midscale
198     clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS);
199     
200   }
201   else
202     putstr("ERR: Invalid Rev\n");
203 }
204
205 // Clock 5 on Rev 3, none (was 2) on Rev 4
206 void
207 clocks_enable_eth_phyclk(bool enable, int divisor)
208 {
209   if(u2_hw_rev_major == 3)
210     clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS);
211   else if(u2_hw_rev_major == 4)
212     clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL);
213   else
214     putstr("ERR: Invalid Rev\n");
215 }
216
217 // Clock 3
218 void
219 clocks_enable_dac_clk(bool enable, int divisor)
220 {
221   clocks_enable_XXX_clk(enable,divisor,0x3F,0x4E,CLOCK_MODE_PECL);
222 }
223
224 // Clock 4
225 void
226 clocks_enable_adc_clk(bool enable, int divisor)
227 {
228   clocks_enable_XXX_clk(enable,divisor,0x40,0x50,CLOCK_MODE_LVDS);
229 }
230
231 // Clock 6
232 void
233 clocks_enable_tx_dboard(bool enable, int divisor)
234 {
235   clocks_enable_XXX_clk(enable,divisor,0x42,0x54,CLOCK_MODE_CMOS);
236 }
237
238 // Clock 7
239 void
240 clocks_enable_rx_dboard(bool enable, int divisor)
241 {
242   clocks_enable_XXX_clk(enable,divisor,0x43,0x56,CLOCK_MODE_CMOS);
243 }