Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8857df3a authored by Michael Hennerich's avatar Michael Hennerich Committed by Jonathan Cameron
Browse files

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: default avatarMichael Hennerich <michael.hennerich@analog.com>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent dfffd0d6
Loading
Loading
Loading
Loading
+15 −9
Original line number Original line Diff line number Diff line
@@ -129,7 +129,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
{
{
	struct adf4350_platform_data *pdata = st->pdata;
	struct adf4350_platform_data *pdata = st->pdata;
	u64 tmp;
	u64 tmp;
	u32 div_gcd, prescaler;
	u32 div_gcd, prescaler, chspc;
	u16 mdiv, r_cnt = 0;
	u16 mdiv, r_cnt = 0;
	u8 band_sel_div;
	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)
	if (pdata->ref_div_factor)
		r_cnt = pdata->ref_div_factor - 1;
		r_cnt = pdata->ref_div_factor - 1;


	do  {
	chspc = st->chspc;
		r_cnt = adf4350_tune_r_cnt(st, r_cnt);


		st->r1_mod = st->fpfd / st->chspc;
	do  {
		while (st->r1_mod > ADF4350_MAX_MODULUS) {
		do {
			do {
				r_cnt = adf4350_tune_r_cnt(st, r_cnt);
				r_cnt = adf4350_tune_r_cnt(st, r_cnt);
			st->r1_mod = st->fpfd / st->chspc;
				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);
		tmp = freq * (u64)st->r1_mod + (st->fpfd > 1);
		do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
		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) |
	st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) |
				 ADF4350_REG0_FRACT(st->r0_fract);
				 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) |
				 ADF4350_REG1_MOD(st->r1_mod) |
				 prescaler;
				 prescaler;


+2 −0
Original line number Original line Diff line number Diff line
@@ -87,6 +87,8 @@
#define ADF4350_MAX_BANDSEL_CLK		125000 /* Hz */
#define ADF4350_MAX_BANDSEL_CLK		125000 /* Hz */
#define ADF4350_MAX_FREQ_REFIN		250000000 /* Hz */
#define ADF4350_MAX_FREQ_REFIN		250000000 /* Hz */
#define ADF4350_MAX_MODULUS		4095
#define ADF4350_MAX_MODULUS		4095
#define ADF4350_MAX_R_CNT		1023



/**
/**
 * struct adf4350_platform_data - platform specific information
 * struct adf4350_platform_data - platform specific information