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
|
/*
* This file is part of the bladeRF project:
* http://www.github.com/nuand/bladeRF
*
* Copyright (C) 2016 Nuand LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "log.h"
#include "smb_clock.h"
#include "driver/si5338.h"
#include "board/board.h"
struct regvals {
uint8_t addr;
uint8_t data;
};
/* Power-on defaults with SMB clock port not in use */
static const struct regvals default_config[] = {
{ 6, 0x08 },
{ 28, 0x0b },
{ 29, 0x08 },
{ 30, 0xb0 },
{ 34, 0xe3 },
{ 39, 0x00 },
/* Reset Multisynth 3 */
{ 86, 0x00 },
{ 87, 0x00 },
{ 88, 0x00 },
{ 89, 0x00 },
{ 90, 0x00 },
{ 91, 0x00 },
{ 92, 0x00 },
{ 93, 0x00 },
{ 94, 0x00 },
{ 95, 0x00 },
};
static const struct regvals input_config[] = {
{ 6, 0x04 },
{ 28, 0x2b },
{ 29, 0x28 },
{ 30, 0xa8 },
};
static const struct regvals output_config[] = {
{ 34, 0x22 },
};
static int write_regs(struct bladerf *dev, const struct regvals *reg, size_t n)
{
size_t i;
int status = 0;
for (i = 0; i < n && status == 0; i++) {
status = dev->backend->si5338_write(dev, reg[i].addr, reg[i].data);
}
return status;
}
static inline int smb_mode_input(struct bladerf *dev)
{
int status;
uint8_t val;
status = write_regs(dev, input_config, ARRAY_SIZE(input_config));
if (status != 0) {
return status;
}
/* Turn off any SMB connector output */
status = dev->backend->si5338_read(dev, 39, &val);
if (status != 0) {
return status;
}
val &= ~(1);
status = dev->backend->si5338_write(dev, 39, val);
if (status != 0) {
return status;
}
return status;
}
static inline int smb_mode_output(struct bladerf *dev)
{
int status;
uint8_t val;
status = dev->backend->si5338_read(dev, 39, &val);
if (status != 0) {
return status;
}
val |= 1;
status = dev->backend->si5338_write(dev, 39, val);
if (status != 0) {
return status;
}
status = write_regs(dev, output_config, ARRAY_SIZE(output_config));
if (status != 0) {
return status;
}
return status;
}
static inline int smb_mode_disabled(struct bladerf *dev)
{
return write_regs(dev, default_config, ARRAY_SIZE(default_config));
}
int smb_clock_set_mode(struct bladerf *dev, bladerf_smb_mode mode)
{
int status;
/* Reset initial state */
status = smb_mode_disabled(dev);
if (status != 0) {
return status;
}
/* Apply changes */
switch (mode) {
case BLADERF_SMB_MODE_DISABLED:
break;
case BLADERF_SMB_MODE_OUTPUT:
status = smb_mode_output(dev);
break;
case BLADERF_SMB_MODE_INPUT:
status = smb_mode_input(dev);
break;
default:
log_debug("Invalid SMB clock port mode: %d\n", mode);
return BLADERF_ERR_INVAL;
}
return status;
}
/* For simplicity, we just check a few bits that are indicative of our known
* register configurations for our SMB clock port configurations.
*
* Inconsistent register states (or users manually changing registers) may
* cause this function to erroneously report the state.
*/
int smb_clock_get_mode(struct bladerf *dev, bladerf_smb_mode *mode)
{
int status;
uint8_t val;
/* Check DRV3_FMT[2:0] for an output configuration */
status = dev->backend->si5338_read(dev, 39, &val);
if (status != 0) {
return status;
}
switch (val & 0x7) {
case 0x00: /* No output */
break;
case 0x01: /* CLK3A CMOS/SSTL/HSTL - we're outputting a clock */
*mode = BLADERF_SMB_MODE_OUTPUT;
return 0;
case 0x02: /* CLK3B CMOS/SSTL/HSTL - used by the XB-200. */
*mode = BLADERF_SMB_MODE_UNAVAILBLE;
return 0;
default:
*mode = BLADERF_SMB_MODE_INVALID;
log_debug("Si5338[39] in unexpected state: 0x%02x\n", val);
return BLADERF_ERR_UNSUPPORTED;
}
/* Check P2DIV_IN[0] for an input configuration */
status = dev->backend->si5338_read(dev, 28, &val);
if (status != 0) {
return status;
}
if ((val & (1 << 5)) != 0) {
*mode = BLADERF_SMB_MODE_INPUT;
} else {
*mode = BLADERF_SMB_MODE_DISABLED;
}
return status;
}
|