Skip to content

Commit 2db71bb

Browse files
committed
drivers: iio: hmc7044: Add OSCOUT as channel
Add OSCOUT1 as an output channel to the driver. The OSCOUT1 output is used a an input for the ADF4371 on the AD9213_EVB, and is used for synchronizing the initalization of the two devices in this setup. Signed-off-by: George Mois <george.mois@analog.com>
1 parent 8ddcfab commit 2db71bb

File tree

1 file changed

+171
-113
lines changed

1 file changed

+171
-113
lines changed

drivers/iio/frequency/hmc7044.c

Lines changed: 171 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@
214214
#define HMC7044_DYN_DRIVER_EN BIT(5)
215215
#define HMC7044_FORCE_MUTE_EN BIT(7)
216216

217-
#define HMC7044_NUM_CHAN 14
217+
#define HMC7044_NUM_CHAN 15
218218

219219
#define HMC7044_LOW_VCO_MIN 2150000
220220
#define HMC7044_LOW_VCO_MAX 2880000
@@ -309,11 +309,6 @@ struct hmc7044 {
309309
bool oscout_path_en;
310310
bool oscout0_driver_en;
311311
bool oscout1_driver_en;
312-
u32 oscout_divider_ratio;
313-
u32 oscout0_driver_mode;
314-
u32 oscout1_driver_mode;
315-
u32 oscout0_driver_impedance;
316-
u32 oscout1_driver_impedance;
317312
unsigned int sync_pin_mode;
318313
unsigned int pulse_gen_mode;
319314
unsigned int in_buf_mode[5];
@@ -449,6 +444,26 @@ static unsigned int hmc7044_calc_out_div(unsigned long parent_rate,
449444
return div;
450445
}
451446

447+
static unsigned int hmc7044_calc_oscout_div(unsigned long parent_rate,
448+
unsigned long rate)
449+
{
450+
unsigned int div;
451+
452+
div = DIV_ROUND_CLOSEST(parent_rate, rate);
453+
454+
/* Supported divide ratios are 1, 2, 4, and 8 */
455+
if (div == 1 || div == 2 || div == 4 || div == 8)
456+
return div;
457+
458+
if (div == 0)
459+
return 1;
460+
461+
if (div % 2)
462+
div--;
463+
464+
return (div == 6 || div > 8) ? 8 : div;
465+
}
466+
452467
static int hmc7044_read_raw(struct iio_dev *indio_dev,
453468
struct iio_chan_spec const *chan,
454469
int *val,
@@ -466,13 +481,20 @@ static int hmc7044_read_raw(struct iio_dev *indio_dev,
466481

467482
switch (mask) {
468483
case IIO_CHAN_INFO_FREQUENCY:
469-
*val = hmc->pll2_freq / ch->divider;
484+
if (ch->num == 14)
485+
*val = hmc->vcxo_freq / ch->divider;
486+
else
487+
*val = hmc->pll2_freq / ch->divider;
470488
return IIO_VAL_INT;
471489
case IIO_CHAN_INFO_PHASE:
472-
hmc7044_read(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(ch->num),
473-
&tmp);
474-
tmp &= 0x1F;
475-
code = DIV_ROUND_CLOSEST(tmp * 3141592, ch->divider);
490+
if (ch->num == 14) {
491+
code = 0;
492+
} else {
493+
hmc7044_read(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(ch->num),
494+
&tmp);
495+
tmp &= 0x1F;
496+
code = DIV_ROUND_CLOSEST(tmp * 3141592, ch->divider);
497+
}
476498
*val = code / 1000000;
477499
*val2 = code % 1000000;
478500
return IIO_VAL_INT_PLUS_MICRO;
@@ -498,22 +520,33 @@ static int hmc7044_write_raw(struct iio_dev *indio_dev,
498520

499521
switch (mask) {
500522
case IIO_CHAN_INFO_FREQUENCY:
501-
ch->divider = hmc7044_calc_out_div(hmc->pll2_freq, val);
502-
mutex_lock(&hmc->lock);
503-
hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_1(ch->num),
504-
HMC7044_DIV_LSB(ch->divider));
505-
hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_2(ch->num),
506-
HMC7044_DIV_MSB(ch->divider));
507-
mutex_unlock(&hmc->lock);
523+
if (ch->num == 14) {
524+
ch->divider = hmc7044_calc_oscout_div(hmc->vcxo_freq, val);
525+
mutex_lock(&hmc->lock);
526+
hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_PATH,
527+
HMC7044_OSCOUT_DIVIDER(ch->divider <= 4 ? ch->divider / 2 : 3) |
528+
hmc->oscout_path_en);
529+
mutex_unlock(&hmc->lock);
530+
} else {
531+
ch->divider = hmc7044_calc_out_div(hmc->pll2_freq, val);
532+
mutex_lock(&hmc->lock);
533+
hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_1(ch->num),
534+
HMC7044_DIV_LSB(ch->divider));
535+
hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_2(ch->num),
536+
HMC7044_DIV_MSB(ch->divider));
537+
mutex_unlock(&hmc->lock);
538+
}
508539
break;
509540
case IIO_CHAN_INFO_PHASE:
510-
mutex_lock(&hmc->lock);
511-
code = val * 1000000 + val2 % 1000000;
512-
tmp = DIV_ROUND_CLOSEST(code * ch->divider, 3141592);
513-
tmp = clamp_t(unsigned int, tmp, 0, 17);
514-
hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(ch->num),
515-
tmp);
516-
mutex_unlock(&hmc->lock);
541+
if (ch->num != 14) {
542+
mutex_lock(&hmc->lock);
543+
code = val * 1000000 + val2 % 1000000;
544+
tmp = DIV_ROUND_CLOSEST(code * ch->divider, 3141592);
545+
tmp = clamp_t(unsigned int, tmp, 0, 17);
546+
hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(ch->num),
547+
tmp);
548+
mutex_unlock(&hmc->lock);
549+
}
517550
break;
518551
default:
519552
return -EINVAL;
@@ -801,6 +834,13 @@ static long hmc7044_set_clk_attr(struct clk_hw *hw,
801834
static unsigned long hmc7044_clk_recalc_rate(struct clk_hw *hw,
802835
unsigned long parent_rate)
803836
{
837+
struct hmc7044_output *out = to_output(hw);
838+
struct iio_dev *indio_dev = out->indio_dev;
839+
struct hmc7044 *hmc = iio_priv(indio_dev);
840+
struct hmc7044_chan_spec *ch;
841+
842+
ch = &hmc->channels[out->address];
843+
804844
return hmc7044_get_clk_attr(hw, IIO_CHAN_INFO_FREQUENCY);
805845
}
806846

@@ -811,11 +851,21 @@ static long hmc7044_clk_round_rate(struct clk_hw *hw,
811851
struct hmc7044_output *out = to_output(hw);
812852
struct iio_dev *indio_dev = out->indio_dev;
813853
struct hmc7044 *hmc = iio_priv(indio_dev);
854+
struct hmc7044_chan_spec *ch;
855+
long rounded_rate;
814856
unsigned int div;
815857

816-
div = hmc7044_calc_out_div(hmc->pll2_freq, rate);
858+
ch = &hmc->channels[out->address];
817859

818-
return DIV_ROUND_CLOSEST(hmc->pll2_freq, div);
860+
if (ch->num == 14) {
861+
div = hmc7044_calc_oscout_div(hmc->vcxo_freq, rate);
862+
rounded_rate = DIV_ROUND_CLOSEST(hmc->vcxo_freq, div);
863+
} else {
864+
div = hmc7044_calc_out_div(hmc->vcxo_freq, rate);
865+
rounded_rate = DIV_ROUND_CLOSEST(hmc->pll2_freq, div);
866+
}
867+
868+
return rounded_rate;
819869
}
820870

821871
static int hmc7044_clk_determine_rate(struct clk_hw *hw,
@@ -824,11 +874,20 @@ static int hmc7044_clk_determine_rate(struct clk_hw *hw,
824874
struct hmc7044_output *out = to_output(hw);
825875
struct iio_dev *indio_dev = out->indio_dev;
826876
struct hmc7044 *hmc = iio_priv(indio_dev);
877+
struct hmc7044_chan_spec *ch;
827878
unsigned int div;
828879

829-
div = hmc7044_calc_out_div(hmc->pll2_freq, req->rate);
880+
ch = &hmc->channels[out->address];
881+
882+
if (ch->num == 14) {
883+
div = hmc7044_calc_oscout_div(hmc->vcxo_freq, req->rate);
884+
885+
req->rate = DIV_ROUND_CLOSEST(hmc->vcxo_freq, div);
886+
} else {
887+
div = hmc7044_calc_out_div(hmc->pll2_freq, req->rate);
830888

831-
req->rate = DIV_ROUND_CLOSEST(hmc->pll2_freq, div);
889+
req->rate = DIV_ROUND_CLOSEST(hmc->pll2_freq, div);
890+
}
832891

833892
return 0;
834893
}
@@ -837,6 +896,13 @@ static int hmc7044_clk_set_rate(struct clk_hw *hw,
837896
unsigned long rate,
838897
unsigned long parent_rate)
839898
{
899+
struct hmc7044_output *out = to_output(hw);
900+
struct iio_dev *indio_dev = out->indio_dev;
901+
struct hmc7044 *hmc = iio_priv(indio_dev);
902+
struct hmc7044_chan_spec *ch;
903+
904+
ch = &hmc->channels[out->address];
905+
840906
return hmc7044_set_clk_attr(hw, IIO_CHAN_INFO_FREQUENCY, rate);
841907
}
842908

@@ -858,7 +924,7 @@ static int hmc7044_clk_register(struct iio_dev *indio_dev,
858924

859925
init.name = hmc->clk_out_names[num];
860926
init.ops = &hmc7044_clk_ops;
861-
init.flags = 0;
927+
init.flags = CLK_GET_RATE_NOCACHE;
862928
init.parent_names = (parent_name ? &parent_name : NULL);
863929
init.num_parents = (parent_name ? 1 : 0);
864930

@@ -1057,6 +1123,12 @@ static int hmc7044_setup(struct iio_dev *indio_dev)
10571123
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_0(i), 0);
10581124
if (ret)
10591125
return ret;
1126+
1127+
if (i == 14) {
1128+
ret = hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_PATH, 0);
1129+
if (ret)
1130+
return ret;
1131+
}
10601132
}
10611133

10621134
/* Load the configuration updates (provided by Analog Devices) */
@@ -1264,43 +1336,75 @@ static int hmc7044_setup(struct iio_dev *indio_dev)
12641336
if (chan->num >= HMC7044_NUM_CHAN || chan->disable)
12651337
continue;
12661338

1267-
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num),
1268-
HMC7044_DIV_LSB(chan->divider));
1269-
if (ret)
1270-
return ret;
1271-
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num),
1272-
HMC7044_DIV_MSB(chan->divider));
1273-
if (ret)
1274-
return ret;
1275-
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_8(chan->num),
1276-
HMC7044_DRIVER_MODE(chan->driver_mode) |
1277-
HMC7044_DRIVER_Z_MODE(chan->driver_impedance) |
1278-
(chan->dynamic_driver_enable ?
1279-
HMC7044_DYN_DRIVER_EN : 0) |
1280-
(chan->force_mute_enable ?
1281-
HMC7044_FORCE_MUTE_EN : 0));
1282-
if (ret)
1283-
return ret;
1284-
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_3(chan->num),
1285-
chan->fine_delay & 0x1F);
1286-
if (ret)
1287-
return ret;
1288-
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(chan->num),
1289-
chan->coarse_delay & 0x1F);
1290-
if (ret)
1291-
return ret;
1292-
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_7(chan->num),
1293-
chan->out_mux_mode & 0x3);
1294-
if (ret)
1295-
return ret;
1296-
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_0(chan->num),
1297-
(chan->start_up_mode_dynamic_enable ?
1298-
HMC7044_START_UP_MODE_DYN_EN : 0) | BIT(4) |
1299-
(chan->high_performance_mode_dis ?
1300-
0 : HMC7044_HI_PERF_MODE) | HMC7044_SYNC_EN |
1301-
HMC7044_CH_EN);
1302-
if (ret)
1303-
return ret;
1339+
if (chan->num < (HMC7044_NUM_CHAN - 1)) {
1340+
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num),
1341+
HMC7044_DIV_LSB(chan->divider));
1342+
if (ret)
1343+
return ret;
1344+
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num),
1345+
HMC7044_DIV_MSB(chan->divider));
1346+
if (ret)
1347+
return ret;
1348+
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_8(chan->num),
1349+
HMC7044_DRIVER_MODE(chan->driver_mode) |
1350+
HMC7044_DRIVER_Z_MODE(chan->driver_impedance) |
1351+
(chan->dynamic_driver_enable ?
1352+
HMC7044_DYN_DRIVER_EN : 0) |
1353+
(chan->force_mute_enable ?
1354+
HMC7044_FORCE_MUTE_EN : 0));
1355+
if (ret)
1356+
return ret;
1357+
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_3(chan->num),
1358+
chan->fine_delay & 0x1F);
1359+
if (ret)
1360+
return ret;
1361+
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(chan->num),
1362+
chan->coarse_delay & 0x1F);
1363+
if (ret)
1364+
return ret;
1365+
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_7(chan->num),
1366+
chan->out_mux_mode & 0x3);
1367+
if (ret)
1368+
return ret;
1369+
ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_0(chan->num),
1370+
(chan->start_up_mode_dynamic_enable ?
1371+
HMC7044_START_UP_MODE_DYN_EN : 0) | BIT(4) |
1372+
(chan->high_performance_mode_dis ?
1373+
0 : HMC7044_HI_PERF_MODE) | HMC7044_SYNC_EN |
1374+
HMC7044_CH_EN);
1375+
if (ret)
1376+
return ret;
1377+
} else {
1378+
if (hmc->oscout_path_en) {
1379+
/* Make sure divider has acceptable value */
1380+
if (chan->divider != 1 && chan->divider != 2 &&
1381+
chan->divider != 4 && chan->divider != 8)
1382+
return -EINVAL;
1383+
ret = hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_PATH,
1384+
HMC7044_OSCOUT_DIVIDER(chan->divider <= 4 ? chan->divider / 2 : 3) |
1385+
HMC7044_CH_EN);
1386+
if (ret)
1387+
return ret;
1388+
}
1389+
1390+
if (hmc->oscout0_driver_en) {
1391+
hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_DRIVER_0,
1392+
HMC7044_OSCOUT_DRIVER_MODE(chan->driver_mode) |
1393+
HMC7044_OSCOUT_IMPEDANCE(chan->driver_impedance) |
1394+
HMC7044_CH_EN);
1395+
if (ret)
1396+
return ret;
1397+
}
1398+
1399+
if (hmc->oscout1_driver_en) {
1400+
hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_DRIVER_1,
1401+
HMC7044_OSCOUT_DRIVER_MODE(chan->driver_mode) |
1402+
HMC7044_OSCOUT_IMPEDANCE(chan->driver_impedance) |
1403+
HMC7044_CH_EN);
1404+
if (ret)
1405+
return ret;
1406+
}
1407+
}
13041408
hmc->iio_channels[i].type = IIO_ALTVOLTAGE;
13051409
hmc->iio_channels[i].output = 1;
13061410
hmc->iio_channels[i].indexed = 1;
@@ -1357,32 +1461,6 @@ static int hmc7044_setup(struct iio_dev *indio_dev)
13571461
hmc->clk_data.clks = hmc->clks;
13581462
hmc->clk_data.clk_num = HMC7044_NUM_CHAN;
13591463

1360-
if (hmc->oscout_path_en) {
1361-
ret = hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_PATH,
1362-
HMC7044_OSCOUT_DIVIDER(hmc->oscout_divider_ratio) |
1363-
hmc->oscout_path_en);
1364-
if (ret)
1365-
return ret;
1366-
}
1367-
1368-
if (hmc->oscout0_driver_en) {
1369-
hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_DRIVER_0,
1370-
HMC7044_OSCOUT_DRIVER_MODE(hmc->oscout1_driver_mode) |
1371-
HMC7044_OSCOUT_IMPEDANCE(hmc->oscout1_driver_impedance) |
1372-
hmc->oscout1_driver_en);
1373-
if (ret)
1374-
return ret;
1375-
}
1376-
1377-
if (hmc->oscout1_driver_en) {
1378-
hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_DRIVER_1,
1379-
HMC7044_OSCOUT_DRIVER_MODE(hmc->oscout1_driver_mode) |
1380-
HMC7044_OSCOUT_IMPEDANCE(hmc->oscout1_driver_impedance) |
1381-
hmc->oscout1_driver_en);
1382-
if (ret)
1383-
return ret;
1384-
}
1385-
13861464
ret = hmc7044_info(indio_dev);
13871465
if (ret)
13881466
return ret;
@@ -1707,30 +1785,10 @@ static int hmc7044_parse_dt(struct device *dev,
17071785
hmc->oscout_path_en =
17081786
of_property_read_bool(np, "adi,oscillator-output-path-enable");
17091787

1710-
hmc->oscout_divider_ratio = HMC7044_DIVIDER_RATIO_1;
1711-
of_property_read_u32(np, "adi,oscillator-output-divider-ratio",
1712-
&hmc->oscout_divider_ratio);
1713-
17141788
hmc->oscout0_driver_en = of_property_read_bool(np, "adi,oscillator-output0-driver-enable");
17151789

1716-
hmc->oscout0_driver_mode = HMC7044_DRIVER_MODE_CML;
1717-
of_property_read_u32(np, "adi,oscillator-output0-driver-mode",
1718-
&hmc->oscout0_driver_mode);
1719-
1720-
hmc->oscout0_driver_impedance = HMC7044_DRIVER_IMPEDANCE_DISABLE;
1721-
of_property_read_u32(np, "adi,oscillator-output0-driver-impedance",
1722-
&hmc->oscout0_driver_impedance);
1723-
17241790
hmc->oscout1_driver_en = of_property_read_bool(np, "adi,oscillator-output1-driver-enable");
17251791

1726-
hmc->oscout1_driver_mode = HMC7044_DRIVER_MODE_CML;
1727-
of_property_read_u32(np, "adi,oscillator-output1-driver-mode",
1728-
&hmc->oscout1_driver_mode);
1729-
1730-
hmc->oscout1_driver_impedance = HMC7044_DRIVER_IMPEDANCE_DISABLE;
1731-
of_property_read_u32(np, "adi,oscillator-output1-driver-impedance",
1732-
&hmc->oscout1_driver_impedance);
1733-
17341792
hmc->sysref_timer_div = 256;
17351793
of_property_read_u32(np, "adi,sysref-timer-divider",
17361794
&hmc->sysref_timer_div);

0 commit comments

Comments
 (0)