Skip to content

Commit 26f5457

Browse files
mhennerichnunojsa
authored andcommitted
iio: hmc7044: Improve LMFC/LEMC rate handling with sub-Hz precision
This patch enhances the JESD204 LMFC/LEMC rate handling in the HMC7044 driver by introducing full support for sub-Hz precision using microhertz (uHz) units. Key changes include: - Conversion of all relevant JESD204 rate fields from u32 to u64 to accommodate higher precision. - Use of MICROHZ_PER_HZ for accurate conversion between Hz and uHz. - Introduction of helper functions `hmc7044_gcd_64()` and `hmc7044_get_rem()` to support 64-bit GCD and remainder operations. - Improved validation logic in `hmc7044_lmfc_lemc_validate()` to work with 64-bit values and ensure correct divisor selection. - Updated device tree parsing to support both Hz and uHz sysref frequency properties. - Enhanced debug logging to reflect the new precision and aid troubleshooting. These changes ensure that the driver can correctly interpret and validate JESD204 timing parameters with high precision, improving compatibility with systems that require fine-grained clock synchronization. Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
1 parent ee97159 commit 26f5457

File tree

1 file changed

+76
-38
lines changed

1 file changed

+76
-38
lines changed

drivers/iio/frequency/hmc7044.c

Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/gcd.h>
1212
#include <linux/rational.h>
1313
#include <linux/debugfs.h>
14+
#include <linux/units.h>
1415

1516
#include <linux/clk.h>
1617
#include <linux/clk-provider.h>
@@ -328,11 +329,11 @@ struct hmc7044 {
328329
struct clk_onecell_data clk_data;
329330
struct clk *clk_input[4];
330331
struct mutex lock;
331-
struct jesd204_dev *jdev;
332-
u32 jdev_lmfc_lemc_rate;
333-
u32 jdev_lmfc_lemc_gcd;
334-
u32 jdev_max_sysref_freq;
335-
u32 jdev_desired_sysref_freq;
332+
struct jesd204_dev *jdev;
333+
u64 jdev_lmfc_lemc_rate;
334+
u64 jdev_lmfc_lemc_gcd;
335+
u64 jdev_max_sysref_freq;
336+
u64 jdev_desired_sysref_freq;
336337
bool jdev_skip_sysref_freq_calc;
337338
bool is_sysref_provider;
338339
bool hmc_two_level_tree_sync_en;
@@ -1600,6 +1601,7 @@ static int hmc7044_parse_dt(struct device *dev,
16001601
struct device_node *np = dev->of_node, *chan_np;
16011602
unsigned int cnt = 0;
16021603
int ret;
1604+
u32 tmp;
16031605

16041606
if (hmc->device_id == HMC7044) {
16051607
ret = of_property_read_u32_array(np,
@@ -1691,12 +1693,17 @@ static int hmc7044_parse_dt(struct device *dev,
16911693

16921694
hmc->hmc_two_level_tree_sync_en = of_property_read_bool(np, "adi,hmc-two-level-tree-sync-en");
16931695

1694-
hmc->jdev_max_sysref_freq = INT_MAX;
1695-
of_property_read_u32(np, "adi,jesd204-max-sysref-frequency-hz",
1696-
&hmc->jdev_max_sysref_freq);
1696+
tmp = INT_MAX;
1697+
of_property_read_u32(np, "adi,jesd204-max-sysref-frequency-hz", &tmp);
1698+
hmc->jdev_max_sysref_freq = (u64)tmp * MICROHZ_PER_HZ;
1699+
1700+
ret = of_property_read_u32(np, "adi,jesd204-desired-sysref-frequency-hz", &tmp);
1701+
if (!ret) {
1702+
hmc->jdev_desired_sysref_freq = (u64)tmp * MICROHZ_PER_HZ;
1703+
}
1704+
1705+
of_property_read_u64(np, "adi,jesd204-desired-sysref-frequency-uhz", &hmc->jdev_desired_sysref_freq);
16971706

1698-
of_property_read_u32(np, "adi,jesd204-desired-sysref-frequency-hz",
1699-
&hmc->jdev_desired_sysref_freq);
17001707

17011708
hmc->jdev_skip_sysref_freq_calc =
17021709
of_property_read_bool(np, "adi,jesd204-skip-sysref-frequency-calc");
@@ -1950,31 +1957,56 @@ static int hmc7044_continuous_chan_sync_enable(struct iio_dev *indio_dev, bool e
19501957
return 0;
19511958
}
19521959

1953-
static int hmc7044_lmfc_lemc_validate(struct hmc7044 *hmc, u64 dividend, u32 divisor)
1960+
static u64 hmc7044_gcd_64(u64 u, u64 v)
1961+
{
1962+
u64 t;
1963+
1964+
while (v != 0) {
1965+
t = u;
1966+
u = v;
1967+
div64_u64_rem(t, v, &v);
1968+
}
1969+
1970+
return u;
1971+
}
1972+
1973+
static u64 hmc7044_get_rem(u64 dividend, u64 divisor)
19541974
{
1955-
u32 rem, rem_l, rem_u, gcd_val, min;
1975+
u64 rem;
19561976

1957-
gcd_val = gcd(dividend, divisor);
1977+
if (divisor == 0)
1978+
return 0;
1979+
1980+
div64_u64_rem(dividend, divisor, &rem);
1981+
return rem;
1982+
}
1983+
1984+
static int hmc7044_lmfc_lemc_validate(struct hmc7044 *hmc, u64 dividend, u64 divisor)
1985+
{
1986+
u64 rem, rem_l, rem_u, gcd_val, min;
1987+
1988+
gcd_val = hmc7044_gcd_64(dividend, divisor);
19581989
min = DIV_ROUND_CLOSEST(hmc->pll2_freq, HMC7044_OUT_DIV_MAX);
19591990

19601991
if (gcd_val >= min) {
19611992
dev_dbg(&hmc->spi->dev,
1962-
"%s: dividend=%llu divisor=%u GCD=%u (hmc->pll2_freq=%u, min=%u)",
1993+
"%s: dividend=%llu divisor=%llu GCD=%llu (hmc->pll2_freq=%u, min=%llu)",
19631994
__func__, dividend, divisor, gcd_val, hmc->pll2_freq, min);
19641995

19651996
hmc->jdev_lmfc_lemc_gcd = gcd_val;
19661997
return 0;
19671998
}
19681999

1969-
div_u64_rem(hmc->pll2_freq, divisor, &rem);
2000+
rem = hmc7044_get_rem(hmc->pll2_freq, divisor);
19702001

19712002
dev_dbg(&hmc->spi->dev,
1972-
"%s: dividend=%llu divisor=%u GCD=%u rem=%u (hmc->pll2_freq=%u)",
2003+
"%s: dividend=%llu divisor=%llu GCD=%llu rem=%llu (hmc->pll2_freq=%u)",
19732004
__func__, dividend, divisor, gcd_val, rem, hmc->pll2_freq);
19742005

1975-
div_u64_rem(dividend, divisor, &rem);
1976-
div_u64_rem(dividend, divisor - 1, &rem_l);
1977-
div_u64_rem(dividend, divisor + 1, &rem_u);
2006+
rem = hmc7044_get_rem(dividend, divisor);
2007+
rem_l = hmc7044_get_rem(dividend, divisor - 1);
2008+
rem_u = hmc7044_get_rem(dividend, divisor + 1);
2009+
19782010

19792011
if ((rem_l > rem) && (rem_u > rem)) {
19802012
if (hmc->jdev_lmfc_lemc_gcd)
@@ -1996,6 +2028,7 @@ static int hmc7044_jesd204_link_supported(struct jesd204_dev *jdev,
19962028
struct hmc7044 *hmc = iio_priv(indio_dev);
19972029
int ret;
19982030
unsigned long rate;
2031+
u64 rate_uHz;
19992032

20002033
if (reason != JESD204_STATE_OP_REASON_INIT) {
20012034
hmc->jdev_lmfc_lemc_rate = 0;
@@ -2014,24 +2047,27 @@ static int hmc7044_jesd204_link_supported(struct jesd204_dev *jdev,
20142047
return -EINVAL;
20152048
}
20162049

2017-
rate = hmc->jdev_desired_sysref_freq;
2050+
rate_uHz = hmc->jdev_desired_sysref_freq;
20182051
} else {
20192052
ret = jesd204_link_get_lmfc_lemc_rate(lnk, &rate);
20202053
if (ret < 0)
20212054
return ret;
2055+
2056+
rate_uHz = (u64)rate * MICROHZ_PER_HZ + ret;
20222057
}
20232058

20242059
if (hmc->jdev_lmfc_lemc_rate) {
2025-
hmc->jdev_lmfc_lemc_rate = min(hmc->jdev_lmfc_lemc_rate, (u32)rate);
2026-
ret = hmc7044_lmfc_lemc_validate(hmc, hmc->jdev_lmfc_lemc_gcd, (u32)rate);
2060+
hmc->jdev_lmfc_lemc_rate = min(hmc->jdev_lmfc_lemc_rate, rate_uHz);
2061+
ret = hmc7044_lmfc_lemc_validate(hmc, hmc->jdev_lmfc_lemc_gcd, rate_uHz);
20272062
} else {
2028-
hmc->jdev_lmfc_lemc_rate = rate;
2029-
ret = hmc7044_lmfc_lemc_validate(hmc, hmc->pll2_freq, (u32)rate);
2063+
hmc->jdev_lmfc_lemc_rate = rate_uHz;
2064+
ret = hmc7044_lmfc_lemc_validate(hmc, (u64)hmc->pll2_freq * MICROHZ_PER_HZ,
2065+
rate_uHz);
20302066
}
20312067

2032-
dev_dbg(dev, "%s:%d link_num %u LMFC/LEMC %u/%lu gcd %u\n",
2068+
dev_dbg(dev, "%s:%d link_num %u LMFC/LEMC %llu/%llu gcd %llu\n",
20332069
__func__, __LINE__, lnk->link_id, hmc->jdev_lmfc_lemc_rate,
2034-
rate, hmc->jdev_lmfc_lemc_gcd);
2070+
rate_uHz, hmc->jdev_lmfc_lemc_gcd);
20352071
if (ret)
20362072
return ret;
20372073

@@ -2199,39 +2235,41 @@ static int hmc7044_jesd204_link_pre_setup(struct jesd204_dev *jdev,
21992235
struct hmc7044 *hmc = iio_priv(indio_dev);
22002236
int i, ret;
22012237
u32 sysref_timer;
2238+
u64 rem;
22022239

22032240
if (reason != JESD204_STATE_OP_REASON_INIT)
22042241
return JESD204_STATE_CHANGE_DONE;
22052242

22062243
dev_dbg(dev, "%s:%d link_num %u\n", __func__, __LINE__, lnk->link_id);
22072244

2208-
if (hmc->jdev_desired_sysref_freq && (hmc->jdev_lmfc_lemc_gcd %
2209-
hmc->jdev_desired_sysref_freq == 0)) {
2245+
rem = hmc7044_get_rem(hmc->jdev_lmfc_lemc_gcd, hmc->jdev_desired_sysref_freq);
2246+
2247+
if (hmc->jdev_desired_sysref_freq && rem == 0) {
22102248
hmc->jdev_lmfc_lemc_gcd = hmc->jdev_desired_sysref_freq;
22112249
} else {
22122250
while ((hmc->jdev_lmfc_lemc_gcd > hmc->jdev_max_sysref_freq) &&
2213-
(hmc->jdev_lmfc_lemc_gcd %
2214-
(hmc->jdev_lmfc_lemc_gcd >> 1) == 0))
2251+
hmc7044_get_rem(hmc->jdev_lmfc_lemc_gcd, hmc->jdev_lmfc_lemc_gcd >> 1) == 0)
22152252
hmc->jdev_lmfc_lemc_gcd >>= 1;
22162253
}
22172254
/* Program the output channels */
22182255
for (i = 0; i < hmc->num_channels; i++) {
22192256
if (hmc->channels[i].start_up_mode_dynamic_enable || hmc->channels[i].is_sysref) {
22202257
long rate;
2258+
unsigned long ccf_rate = DIV_ROUND_CLOSEST_ULL(hmc->jdev_lmfc_lemc_gcd, MICROHZ_PER_HZ);
22212259

2222-
dev_dbg(dev, "%s:%d Found SYSREF channel%u setting f=%u Hz\n",
2223-
__func__, __LINE__, hmc->channels[i].num, hmc->jdev_lmfc_lemc_gcd);
2260+
dev_dbg(dev, "%s:%d Found SYSREF channel%u setting f=%lu Hz\n",
2261+
__func__, __LINE__, hmc->channels[i].num, ccf_rate);
22242262

2225-
rate = clk_round_rate(hmc->clks[hmc->channels[i].num], hmc->jdev_lmfc_lemc_gcd);
2263+
rate = clk_round_rate(hmc->clks[hmc->channels[i].num], ccf_rate);
22262264

2227-
if (rate == (long)hmc->jdev_lmfc_lemc_gcd)
2228-
ret = clk_set_rate(hmc->clks[hmc->channels[i].num], hmc->jdev_lmfc_lemc_gcd);
2265+
if (rate == (long) ccf_rate)
2266+
ret = clk_set_rate(hmc->clks[hmc->channels[i].num], ccf_rate);
22292267
else
22302268
ret = -EINVAL;
22312269

22322270
if (ret < 0)
2233-
dev_err(dev, "%s: Link%u setting SYSREF rate %u failed (%d)\n",
2234-
__func__, lnk->link_id, hmc->jdev_lmfc_lemc_gcd, ret);
2271+
dev_err(dev, "%s: Link%u setting SYSREF rate %lu failed (%d)\n",
2272+
__func__, lnk->link_id, ccf_rate, ret);
22352273

22362274
}
22372275
}
@@ -2241,7 +2279,7 @@ static int hmc7044_jesd204_link_pre_setup(struct jesd204_dev *jdev,
22412279
* output SYSREF frequency, and program it to be no faster than 4 MHz.
22422280
*/
22432281

2244-
sysref_timer = hmc->jdev_lmfc_lemc_gcd / 2;
2282+
sysref_timer = DIV_ROUND_CLOSEST_ULL(hmc->jdev_lmfc_lemc_gcd, MICROHZ_PER_HZ) / 2;
22452283

22462284
while (sysref_timer >= 4000000U)
22472285
sysref_timer >>= 1;

0 commit comments

Comments
 (0)