Skip to content

Commit 148aafd

Browse files
committed
iio: adc: ad9088: Add fractional NCO tuning word support for CNCO and FNCO
This patch updates the NCO frequency read/write logic to support fractional tuning word components (`frac_a` and `frac_b`) for both coarse (CNCO) and fine (FNCO) NCOs. The new implementation uses the updated `adi_ad9088_calc_nco_ftw()` and `adi_ad9088_calc_nco_freq()` APIs, which support arbitrary bit widths and fractional decomposition. This improves frequency resolution and aligns with the hardware's extended tuning capabilities. The fractional components are now stored in the profile and programmed via `adi_apollo_cnco_mod_set()` and `adi_apollo_fnco_main_pgm()`. Signed-off-by: Michael Hennerich <[email protected]>
1 parent 39f5b6f commit 148aafd

File tree

3 files changed

+103
-205
lines changed

3 files changed

+103
-205
lines changed

drivers/iio/adc/apollo/ad9088.c

Lines changed: 91 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -21,188 +21,66 @@ static int ad9088_jesd204_post_setup_stage2(struct jesd204_dev *jdev,
2121
static int ad9088_jesd204_post_setup_stage3(struct jesd204_dev *jdev,
2222
enum jesd204_state_op_reason reason);
2323

24-
void adi_ad9088_hal_add_128(u64 ah, uint64_t al, uint64_t bh, uint64_t bl,
25-
u64 *hi, uint64_t *lo)
26-
{
27-
u64 rl = al + bl, rh = ah + bh;
28-
29-
if (rl < al)
30-
rh++;
31-
*lo = rl;
32-
*hi = rh;
33-
}
34-
35-
void adi_ad9088_hal_sub_128(u64 ah, uint64_t al, uint64_t bh, uint64_t bl,
36-
u64 *hi, uint64_t *lo)
37-
{
38-
u64 rl, rh;
39-
40-
if (bl <= al) {
41-
rl = al - bl;
42-
rh = ah - bh;
43-
} else {
44-
rl = bl - al - 1;
45-
rl = 0xffffffffffffffffull - rl;
46-
ah--;
47-
rh = ah - bh;
48-
}
49-
*lo = rl;
50-
*hi = rh;
51-
}
52-
53-
void adi_ad9088_hal_mult_128(u64 a, uint64_t b, uint64_t *hi, uint64_t *lo)
54-
{
55-
u64 ah = a >> 32, al = a & 0xffffffff, bh = b >> 32,
56-
bl = b & 0xffffffff, rh = ah * bh, rl = al * bl, rm1 = ah * bl,
57-
rm2 = al * bh, rm1h = rm1 >> 32, rm2h = rm2 >> 32,
58-
rm1l = rm1 & 0xffffffff, rm2l = rm2 & 0xffffffff,
59-
rmh = rm1h + rm2h, rml = rm1l + rm2l,
60-
c = ((rl >> 32) + rml) >> 32;
61-
rl = rl + (rml << 32);
62-
rh = rh + rmh + c;
63-
*lo = rl;
64-
*hi = rh;
65-
}
66-
67-
void adi_ad9088_hal_lshift_128(u64 *hi, uint64_t *lo)
68-
{
69-
*hi <<= 1;
70-
if (*lo & 0x8000000000000000ull)
71-
*hi |= 1ull;
72-
*lo <<= 1;
73-
}
74-
75-
void adi_ad9088_hal_rshift_128(u64 *hi, uint64_t *lo)
76-
{
77-
*lo >>= 1;
78-
if (*hi & 1ull)
79-
*lo |= 0x8000000000000000ull;
80-
*hi >>= 1;
81-
}
82-
83-
void adi_ad9088_hal_div_128(u64 a_hi, uint64_t a_lo, uint64_t b_hi,
84-
u64 b_lo, uint64_t *hi, uint64_t *lo)
85-
{
86-
u64 remain_lo = a_lo, remain_hi = a_hi, part1_lo = b_lo,
87-
part1_hi = b_hi;
88-
u64 result_lo = 0, result_hi = 0, mask_lo = 1, mask_hi = 0;
89-
90-
while (!(part1_hi & 0x8000000000000000ull)) {
91-
adi_ad9088_hal_lshift_128(&part1_hi, &part1_lo);
92-
adi_ad9088_hal_lshift_128(&mask_hi, &mask_lo);
93-
}
94-
95-
do {
96-
if ((remain_hi > part1_hi) ||
97-
((remain_hi == part1_hi) && (remain_lo >= part1_lo))) {
98-
adi_ad9088_hal_sub_128(remain_hi, remain_lo, part1_hi,
99-
part1_lo, &remain_hi,
100-
&remain_lo);
101-
adi_ad9088_hal_add_128(result_hi, result_lo, mask_hi,
102-
mask_lo, &result_hi, &result_lo);
103-
}
104-
adi_ad9088_hal_rshift_128(&part1_hi, &part1_lo);
105-
adi_ad9088_hal_rshift_128(&mask_hi, &mask_lo);
106-
} while ((mask_hi != 0) || (mask_lo != 0));
107-
*lo = result_lo;
108-
*hi = result_hi;
109-
}
110-
11124
int32_t adi_ad9088_calc_nco_ftw(adi_apollo_device_t *device,
112-
u64 freq, int64_t nco_shift,
113-
uint64_t *ftw)
25+
u64 freq, s64 nco_shift, u32 bits,
26+
u64 *ftw, u64 *frac_a, u64 *frac_b)
11427
{
115-
u64 hi, lo;
28+
bool neg = false;
29+
int ret;
11630

117-
if (!freq)
31+
pr_debug("adi_ad9088_calc_nco_ftw: freq=%llu, nco_shift=%lld, bits=%u\n",
32+
freq, nco_shift, bits);
33+
34+
if (!freq || !bits || (bits > 64) || !ftw || !frac_a || !frac_b)
11835
return -EINVAL;
11936

12037
nco_shift = clamp_t(int64_t, nco_shift, -(freq >> 1), freq >> 1);
12138

122-
if (nco_shift >= 0) {
123-
adi_ad9088_hal_mult_128(281474976710656ull, nco_shift, &hi,
124-
&lo);
125-
adi_ad9088_hal_add_128(hi, lo, 0, freq >> 1, &hi, &lo);
126-
adi_ad9088_hal_div_128(hi, lo, 0, freq, &hi, ftw);
127-
} else {
128-
adi_ad9088_hal_mult_128(281474976710656ull, -nco_shift, &hi,
129-
&lo);
130-
adi_ad9088_hal_add_128(hi, lo, 0, freq >> 1, &hi, &lo);
131-
adi_ad9088_hal_div_128(hi, lo, 0, freq, &hi, ftw);
132-
*ftw = 281474976710656ull - *ftw;
133-
}
134-
135-
return API_CMS_ERROR_OK;
136-
}
137-
138-
int32_t adi_ad9088_calc_nco_freq(adi_apollo_device_t *device,
139-
u64 freq,
140-
u64 ftw, int64_t *nco_shift)
141-
{
142-
u64 hi, lo;
143-
bool neg = false;
144-
145-
if (!freq)
146-
return -EINVAL;
147-
148-
if (ftw > 140737488355328ull) {
149-
ftw = 0x1000000000000 - ftw;
39+
if (nco_shift < 0) {
40+
nco_shift = -nco_shift;
15041
neg = true;
15142
}
15243

153-
adi_ad9088_hal_mult_128(freq, ftw, &hi, &lo);
154-
adi_ad9088_hal_add_128(hi, lo, 0, 281474976710656ull / 2, &hi, &lo);
155-
adi_ad9088_hal_div_128(hi, lo, 0, 281474976710656ull, &hi, nco_shift);
44+
ret = adi_api_utils_ratio_decomposition(nco_shift, freq, bits, ftw, frac_a, frac_b);
45+
if (ret) {
46+
pr_err("Error in ratio decomposition: %d\n", ret);
47+
return ret;
48+
}
49+
/* frac_a and fact_b are 24-bit registers */
50+
while (*frac_a >= (1 << 24) || *frac_b >= (1 << 24)) {
51+
*frac_a >>= 1;
52+
*frac_b >>= 1;
53+
};
15654

15755
if (neg)
158-
*nco_shift *= -1;
56+
*ftw = (1ULL << bits) - *ftw;
15957

16058
return API_CMS_ERROR_OK;
16159
}
16260

163-
int32_t adi_ad9088_calc_nco_ftw32(adi_apollo_device_t *device,
164-
u64 freq, int64_t nco_shift,
165-
uint64_t *ftw)
166-
{
167-
u64 hi, lo;
168-
169-
if (!freq)
170-
return -EINVAL;
171-
172-
nco_shift = clamp_t(int64_t, nco_shift, -(freq >> 1), freq >> 1);
173-
174-
if (nco_shift >= 0) {
175-
adi_ad9088_hal_mult_128(4294967296ull, nco_shift, &hi, &lo);
176-
adi_ad9088_hal_add_128(hi, lo, 0, freq >> 1, &hi, &lo);
177-
adi_ad9088_hal_div_128(hi, lo, 0, freq, &hi, ftw);
178-
} else {
179-
adi_ad9088_hal_mult_128(4294967296ull, -nco_shift, &hi, &lo);
180-
adi_ad9088_hal_add_128(hi, lo, 0, freq >> 1, &hi, &lo);
181-
adi_ad9088_hal_div_128(hi, lo, 0, freq, &hi, ftw);
182-
*ftw = 4294967296ull - *ftw;
183-
}
184-
185-
return API_CMS_ERROR_OK;
186-
}
187-
188-
int32_t adi_ad9088_calc_nco32_freq(adi_apollo_device_t *device,
189-
u64 freq,
190-
u64 ftw, int64_t *nco_shift)
61+
int32_t adi_ad9088_calc_nco_freq(adi_apollo_device_t *device,
62+
u64 freq, u64 ftw, u32 a, u32 b,
63+
u32 bits, s64 *nco_shift)
19164
{
192-
u64 hi, lo;
65+
u64 hi, lo, mod;
19366
bool neg = false;
19467

195-
if (!freq)
68+
pr_debug("adi_ad9088_calc_nco_freq: freq=%llu, ftw=%llu, a=%u, b=%u, bits=%u\n",
69+
freq, ftw, a, b, bits);
70+
71+
if (!freq || !bits || (bits > 64) || (a > b) || !b)
19672
return -EINVAL;
19773

198-
if (ftw > 2147483648ull) {
199-
ftw = 0x100000000 - ftw;
74+
mod = (1ULL << bits);
75+
76+
if (ftw > (mod >> 1)) {
77+
ftw = mod - ftw;
20078
neg = true;
20179
}
20280

203-
adi_ad9088_hal_mult_128(freq, ftw, &hi, &lo);
204-
adi_ad9088_hal_add_128(hi, lo, 0, 4294967296ull / 2, &hi, &lo);
205-
adi_ad9088_hal_div_128(hi, lo, 0, 4294967296ull, &hi, nco_shift);
81+
adi_api_utils_mult_128(freq, (ftw * 100ULL) + ((100 * a) / b), &hi, &lo);
82+
adi_api_utils_add_128(hi, lo, 0, (mod * 100) >> 1, &hi, &lo);
83+
adi_api_utils_div_128(hi, lo, 0, (mod * 100), &hi, nco_shift);
20684

20785
if (neg)
20886
*nco_shift *= -1;
@@ -903,36 +781,43 @@ static ssize_t ad9088_ext_info_read(struct iio_dev *indio_dev,
903781

904782
switch (private) {
905783
case CDDC_NCO_FREQ:
906-
907-
if (chan->output)
908-
adi_ad9088_calc_nco32_freq(&phy->ad9088, phy->profile.dac_config[side].dac_sampling_rate_Hz,
909-
phy->profile.tx_path[side].tx_cduc[cddc_num].nco[0].nco_phase_inc, &val);
910-
else
911-
adi_ad9088_calc_nco32_freq(&phy->ad9088, phy->profile.adc_config[side].adc_sampling_rate_Hz,
912-
phy->profile.rx_path[side].rx_cddc[cddc_num].nco[0].nco_phase_inc, &val);
913-
914-
//val = phy->cnco_freq[chan->output][side][cddc_num];
915-
ret = 0;
784+
if (chan->output) {
785+
f = phy->profile.dac_config[side].dac_sampling_rate_Hz;
786+
ret = adi_ad9088_calc_nco_freq(&phy->ad9088, f, phy->profile.tx_path[side].tx_cduc[cddc_num].nco[0].nco_phase_inc,
787+
phy->profile.tx_path[side].tx_cduc[cddc_num].nco[0].nco_phase_inc_frac_a,
788+
phy->profile.tx_path[side].tx_cduc[cddc_num].nco[0].nco_phase_inc_frac_b, 32, &val);
789+
} else {
790+
f = phy->profile.adc_config[side].adc_sampling_rate_Hz;
791+
ret = adi_ad9088_calc_nco_freq(&phy->ad9088, f, phy->profile.rx_path[side].rx_cddc[cddc_num].nco[0].nco_phase_inc,
792+
phy->profile.rx_path[side].rx_cddc[cddc_num].nco[0].nco_phase_inc_frac_a,
793+
phy->profile.rx_path[side].rx_cddc[cddc_num].nco[0].nco_phase_inc_frac_b, 32, &val);
794+
}
916795
break;
917796
case FDDC_NCO_FREQ:
918-
919797
if (chan->output) {
920798
u32 cddc_dcm;
921799

922800
adi_apollo_cduc_interp_bf_to_val(&phy->ad9088, phy->profile.tx_path[side].tx_cduc[cddc_num].drc_ratio, &cddc_dcm);
923801
f = phy->profile.dac_config[side].dac_sampling_rate_Hz;
924802
do_div(f, cddc_dcm);
925-
adi_ad9088_calc_nco_freq(&phy->ad9088, f, phy->profile.tx_path[side].tx_fduc[fddc_num].nco[0].nco_phase_inc, &val);
803+
804+
ret = adi_ad9088_calc_nco_freq(&phy->ad9088, f,
805+
phy->profile.tx_path[side].tx_fduc[fddc_num].nco[0].nco_phase_inc,
806+
phy->profile.tx_path[side].tx_fduc[fddc_num].nco[0].nco_phase_inc_frac_a,
807+
phy->profile.tx_path[side].tx_fduc[fddc_num].nco[0].nco_phase_inc_frac_b,
808+
48, &val);
926809
} else {
927810
u32 cddc_dcm;
928811

929812
adi_apollo_cddc_dcm_bf_to_val(&phy->ad9088, phy->profile.rx_path[side].rx_cddc[cddc_num].drc_ratio, &cddc_dcm);
930813
f = phy->profile.adc_config[side].adc_sampling_rate_Hz;
931814
do_div(f, cddc_dcm);
932-
adi_ad9088_calc_nco_freq(&phy->ad9088, f, phy->profile.rx_path[side].rx_fddc[fddc_num].nco[0].nco_phase_inc, &val);
815+
ret = adi_ad9088_calc_nco_freq(&phy->ad9088, f,
816+
phy->profile.rx_path[side].rx_fddc[fddc_num].nco[0].nco_phase_inc,
817+
phy->profile.rx_path[side].rx_fddc[fddc_num].nco[0].nco_phase_inc_frac_a,
818+
phy->profile.rx_path[side].rx_fddc[fddc_num].nco[0].nco_phase_inc_frac_b,
819+
48, &val);
933820
}
934-
//val = phy->fnco_freq[chan->output][side][fddc_num];
935-
ret = 0;
936821
break;
937822
case CDDC_NCO_FREQ_AVAIL:
938823
if (chan->output)
@@ -1036,7 +921,7 @@ static ssize_t ad9088_ext_info_write(struct iio_dev *indio_dev,
1036921
u32 cddc_mask, fddc_mask;
1037922
s32 val32, tmp;
1038923
s64 val64;
1039-
u64 ftw, f;
924+
u64 ftw, f, frac_a, frac_b;
1040925
adi_apollo_terminal_e terminal;
1041926
adi_apollo_cfir_sel_e cfir_sel;
1042927
adi_apollo_cfir_dp_sel dp_sel;
@@ -1053,27 +938,33 @@ static ssize_t ad9088_ext_info_write(struct iio_dev *indio_dev,
1053938
return ret;
1054939

1055940
if (chan->output)
1056-
adi_ad9088_calc_nco_ftw32(&phy->ad9088, phy->profile.dac_config[side].dac_sampling_rate_Hz, readin, &ftw);
941+
f = phy->profile.dac_config[side].dac_sampling_rate_Hz;
1057942
else
1058-
adi_ad9088_calc_nco_ftw32(&phy->ad9088, phy->profile.adc_config[side].adc_sampling_rate_Hz, readin, &ftw);
943+
f = phy->profile.adc_config[side].adc_sampling_rate_Hz;
944+
945+
946+
ret = adi_ad9088_calc_nco_ftw(&phy->ad9088, f, readin, 32, &ftw, &frac_a, &frac_b);
947+
if (ret)
948+
return ret;
1059949

1060-
ret = adi_apollo_cnco_mode_set(&phy->ad9088, chan->output ? ADI_APOLLO_TX : ADI_APOLLO_RX, cddc_mask,
1061-
readin ? ADI_APOLLO_MXR_VAR_IF_MODE : ADI_APOLLO_MXR_ZERO_IF_MODE);
950+
ret = adi_apollo_cnco_ftw_set(&phy->ad9088, chan->output ? ADI_APOLLO_TX : ADI_APOLLO_RX, cddc_mask, 0, 1, (u32) ftw);
1062951
if (ret)
1063952
return ret;
1064953

1065-
ret = adi_apollo_cnco_ftw_set(&phy->ad9088, chan->output ? ADI_APOLLO_TX : ADI_APOLLO_RX, cddc_mask, 0, 1, ftw);
954+
ret = adi_apollo_cnco_mod_set(&phy->ad9088, chan->output ? ADI_APOLLO_TX : ADI_APOLLO_RX, cddc_mask, (u32) frac_a, (u32) frac_b);
1066955

1067956
if (!ret) {
1068-
if (chan->output)
957+
if (chan->output) {
1069958
phy->profile.tx_path[side].tx_cduc[cddc_num].nco[0].nco_phase_inc = ftw;
1070-
else
959+
phy->profile.tx_path[side].tx_cduc[cddc_num].nco[0].nco_phase_inc_frac_a = frac_a;
960+
phy->profile.tx_path[side].tx_cduc[cddc_num].nco[0].nco_phase_inc_frac_b = frac_b;
961+
} else {
1071962
phy->profile.rx_path[side].rx_cddc[cddc_num].nco[0].nco_phase_inc = ftw;
963+
phy->profile.rx_path[side].rx_cddc[cddc_num].nco[0].nco_phase_inc_frac_a = frac_a;
964+
phy->profile.rx_path[side].rx_cddc[cddc_num].nco[0].nco_phase_inc_frac_b = frac_b;
965+
}
1072966
}
1073967

1074-
// if (!ret)
1075-
// phy->cnco_freq[chan->output][side][cddc_num] = readin;
1076-
1077968
break;
1078969
case FDDC_NCO_FREQ:
1079970
ret = kstrtoll(buf, 10, &readin);
@@ -1094,25 +985,30 @@ static ssize_t ad9088_ext_info_write(struct iio_dev *indio_dev,
1094985
do_div(f, cddc_dcm);
1095986
}
1096987

1097-
adi_ad9088_calc_nco_ftw(&phy->ad9088, f, readin, &ftw);
1098-
1099-
ret = adi_apollo_fnco_mode_set(&phy->ad9088, chan->output ? ADI_APOLLO_TX : ADI_APOLLO_RX, fddc_mask,
1100-
readin ? ADI_APOLLO_MXR_VAR_IF_MODE : ADI_APOLLO_MXR_ZERO_IF_MODE);
988+
ret = adi_ad9088_calc_nco_ftw(&phy->ad9088, f, readin, 48, &ftw, &frac_a, &frac_b);
1101989
if (ret)
1102990
return ret;
1103991

1104-
ret = adi_apollo_fnco_main_phase_inc_set(&phy->ad9088, chan->output ? ADI_APOLLO_TX : ADI_APOLLO_RX, fddc_mask, ftw);
992+
adi_apollo_fine_nco_main_pgm_t config = {
993+
.main_phase_inc = ftw,
994+
.main_phase_offset = div_s64(phy->fnco_phase[chan->output][side][fddc_num] * 14073748835533, 18000LL),
995+
.drc_phase_inc_frac_a = frac_a,
996+
.drc_phase_inc_frac_b = frac_b,
997+
};
998+
999+
ret = adi_apollo_fnco_main_pgm(&phy->ad9088, chan->output ? ADI_APOLLO_TX : ADI_APOLLO_RX, fddc_mask, &config);
11051000

11061001
if (!ret) {
1107-
if (chan->output)
1002+
if (chan->output) {
11081003
phy->profile.tx_path[side].tx_fduc[fddc_num].nco[0].nco_phase_inc = ftw;
1109-
else
1004+
phy->profile.tx_path[side].tx_fduc[fddc_num].nco[0].nco_phase_inc_frac_a = frac_a;
1005+
phy->profile.tx_path[side].tx_fduc[fddc_num].nco[0].nco_phase_inc_frac_b = frac_b;
1006+
} else {
11101007
phy->profile.rx_path[side].rx_fddc[fddc_num].nco[0].nco_phase_inc = ftw;
1008+
phy->profile.rx_path[side].rx_fddc[fddc_num].nco[0].nco_phase_inc_frac_a = frac_a;
1009+
phy->profile.rx_path[side].rx_fddc[fddc_num].nco[0].nco_phase_inc_frac_b = frac_b;
1010+
}
11111011
}
1112-
1113-
// if (!ret)
1114-
// phy->fnco_freq[chan->output][side][fddc_num] = readin;
1115-
11161012
break;
11171013
case CDDC_NCO_PHASE:
11181014
ret = kstrtoll(buf, 10, &readin);

0 commit comments

Comments
 (0)