summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Hennerich <michael.hennerich@analog.com>2012-07-20 09:31:00 +0100
committerJonathan Cameron <jic23@kernel.org>2012-08-16 20:24:33 +0100
commit8857df3aceb7a8eb7558059b7da109e41dd1fb95 (patch)
treeba45e6d7779aab84f6a7c1ba37697d9914158b97
parentdfffd0d65fdf16d034681716dcbea74776f62e40 (diff)
iio: frequency: ADF4350: Fix potential reference div factor overflow.
With small channel spacing values and high reference frequencies it is possible to exceed the range of the 10-bit counter. Workaround by checking the range and widening some constrains. We don't use the REG1_PHASE value in this case the datasheet recommends to set it to 1 if not used. Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--drivers/iio/frequency/adf4350.c24
-rw-r--r--include/linux/iio/frequency/adf4350.h2
2 files changed, 17 insertions, 9 deletions
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index 59fbb3ae40e7..e35bb8f6fe75 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -129,7 +129,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
{
struct adf4350_platform_data *pdata = st->pdata;
u64 tmp;
- u32 div_gcd, prescaler;
+ u32 div_gcd, prescaler, chspc;
u16 mdiv, r_cnt = 0;
u8 band_sel_div;
@@ -158,14 +158,20 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
if (pdata->ref_div_factor)
r_cnt = pdata->ref_div_factor - 1;
- do {
- r_cnt = adf4350_tune_r_cnt(st, r_cnt);
+ chspc = st->chspc;
- st->r1_mod = st->fpfd / st->chspc;
- while (st->r1_mod > ADF4350_MAX_MODULUS) {
- r_cnt = adf4350_tune_r_cnt(st, r_cnt);
- st->r1_mod = st->fpfd / st->chspc;
- }
+ do {
+ do {
+ do {
+ r_cnt = adf4350_tune_r_cnt(st, r_cnt);
+ st->r1_mod = st->fpfd / chspc;
+ if (r_cnt > ADF4350_MAX_R_CNT) {
+ /* try higher spacing values */
+ chspc++;
+ r_cnt = 0;
+ }
+ } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt);
+ } while (r_cnt == 0);
tmp = freq * (u64)st->r1_mod + (st->fpfd > 1);
do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
@@ -194,7 +200,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) |
ADF4350_REG0_FRACT(st->r0_fract);
- st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(0) |
+ st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(1) |
ADF4350_REG1_MOD(st->r1_mod) |
prescaler;
diff --git a/include/linux/iio/frequency/adf4350.h b/include/linux/iio/frequency/adf4350.h
index b76b4a87065e..be91f344d5fc 100644
--- a/include/linux/iio/frequency/adf4350.h
+++ b/include/linux/iio/frequency/adf4350.h
@@ -87,6 +87,8 @@
#define ADF4350_MAX_BANDSEL_CLK 125000 /* Hz */
#define ADF4350_MAX_FREQ_REFIN 250000000 /* Hz */
#define ADF4350_MAX_MODULUS 4095
+#define ADF4350_MAX_R_CNT 1023
+
/**
* struct adf4350_platform_data - platform specific information