Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iio: frequency: New driver for HMC7044 #141

Merged
merged 2 commits into from
Jul 6, 2018
Merged

iio: frequency: New driver for HMC7044 #141

merged 2 commits into from
Jul 6, 2018

Conversation

dbogdan
Copy link
Contributor

@dbogdan dbogdan commented Jul 4, 2018

HMC7044 is a high performance, dual-loop, integer-N jitter attenuator
capable of performing reference selection and generation of ultralow
phase noise frequencies for high speed data converters with either
parallel or serial (JESD204B type) interfaces.

Signed-off-by: Dragos Bogdan [email protected]

Copy link

@lclausen-adi lclausen-adi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good, well done.

One thing you should review though is the locking (or rather the lack of it). There are code paths that can run concurrently that need locking.

div = clamp(div, 1, 4094);
/* Supported odd divide ratios are 1, 3, and 5 */
if ((div != 1) && (div != 3) && (div != 5))
div = div - (div % 2);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make sure that still pick the divider that gives the closest result. E.g. if the ratio of pll2 and output frequency is lets say 7.4 then DIV_ROUND_CLOSEST() picks 7. Then we clear the LSB and end up with 6. But in this case 8 would have been the better choice.

I think DIV_ROUND_CLOSEST(hmc->pll2_freq, val * 2) * 2 will produce the correct result.
Maybe there is a better way.


#define HMC7044_LOW_VCO_MIN 2150000
#define HMC7044_LOW_VCO_MAX 2880000
#define HMC7044_HIGH_VCO_MIN 2400000

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

datasheet has 2650 here.

int div;

div = DIV_ROUND_CLOSEST(hmc->pll2_freq, rate);
div = clamp(div, 1, 4094);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid the duplicated code here and in write_raw(). Best is to factor it out into a helper function.

hmc->clk_data.clks = hmc->clks;
hmc->clk_data.clk_num = HMC7044_NUM_CHAN;

ret = of_clk_add_provider(hmc->spi->dev.of_node,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just return of_clk...

}

return -EINVAL;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra newline

int i;

for (i = 0; i < hmc->num_channels; i++) {
if (hmc->channels[i].num == chan->channel)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just store the index in the address field of the channel instead of having to loop over the array?

hmc->iio_channels[i].info_mask_separate =
BIT(IIO_CHAN_INFO_FREQUENCY);

ret = hmc7044_clk_register(indio_dev, chan->num);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only register the clocks after all registers have written and the sync has been performed. To avoid race conditions.

if (ret)
return ret;

ret = iio_device_register(indio_dev);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return iio_device_register()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

devm_iio_device_register() could be used as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or hmm ; is the order of unregister important here in the hmc7044_remove() ?
in that case it may be fine to leave this as is;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, why not use devm_iio_device_register() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the un-registration order is important... I might be wrong.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is important

of the master timer, which controls synchronization and
pulse generator events. It should be set to a submultiple
of the lowest output SYSREF frequency, no faster than 4
MHz. If this is, 1024 will be used by default.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is what? ;)

buf[1] = cmd & 0xFF;
buf[2] = val;

ret = spi_write(hmc->spi, buf, 3);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could return directly here with return spi_write(hmc->spi, buf, ARRAY_SIZE(buf));

@@ -0,0 +1,111 @@
Analog Devices HMC7044 device driver
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bindings should be in a different commit

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this.

Copy link
Contributor

@stefpopa stefpopa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good overall

* Copyright 2018 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use the SPDX-License-Identifer at the top following Documentation/process/license-rules.rst
Something like this: // SPDX-License-Identifier: GPL-2.0

u32 clkin_freq[4];
u32 vcxo_freq;
u32 pll2_freq;
unsigned int pll1_loop_bw;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: you are mixing u32 and unsigned int;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've decided to do this to avoid some extra casts - of of_property_read_u32() returns an u32 while the clock framework uses "unsigned long" types.


static int hmc7044_write(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int val)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: you have a double space here

else
high_vco_en = false;


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra line here

return 0;
}

static int hmc7044_setup(struct iio_dev *indio_dev)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking if it wouldn't be better to split this function into smaller pieces. It took a long time to scroll to the end of it :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since no part of this function is called somewhere else, I wouldn't split it. However, if the majority will want this, I will do it. :)


hmc->pll1_loop_bw = 200;
of_property_read_u32(np, "adi,pll1-loop-bandwidth-hz",
&hmc->pll1_loop_bw);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way to deal with optional properties would be something like this:
ret = of_property_read_...();
if (ret) {
...
} else {
...
}

if (ret)
return ret;

ret = iio_device_register(indio_dev);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, why not use devm_iio_device_register() ?

*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you sort this includes to have a logic order:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>

@dbogdan dbogdan force-pushed the hmc7044 branch 6 times, most recently from 9197e8a to 3e01e56 Compare July 5, 2018 12:45
@dbogdan
Copy link
Contributor Author

dbogdan commented Jul 5, 2018

v2:

  • Add mutex locking;
  • Calculation of output dividers was revised;
  • Fix HMC7044_HIGH_VCO_MIN value;
  • Store the iio_channel index to address field of the iio_chan_spec;
  • Register the clocks after the part was initialized;
  • Remove unnecessary includes;
  • Remove extra spaces;
  • Split the commit in two: a dedicated one for the documentation.

Copy link
Contributor

@stefpopa stefpopa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


div = DIV_ROUND_CLOSEST(parent_rate, rate);
/* Supported odd divide ratios are 1, 3, and 5 */
if ((div != 1) && (div != 3) && (div != 5)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should only re-calc if the div is uneven.

char name[SPI_NAME_SIZE + 8];

sprintf(name, "%s_out%d", indio_dev->name, num);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as before. Should read clock-output-names from devicetree.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good remarked - fixed this.

#define HMC7044_OUT_DIV_MIN 1
#define HMC7044_OUT_DIV_MAX 4094

struct output {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer that all the structs have a proper hmc7044_ prefix.

@dbogdan dbogdan force-pushed the hmc7044 branch 2 times, most recently from dbdbf65 to bacf9c4 Compare July 5, 2018 16:06
@dbogdan
Copy link
Contributor Author

dbogdan commented Jul 5, 2018

v3:

  • add hmc7044_ prefix to output and chan_spec structures;
  • compute the divider only for the odd numbers;
  • get the clock-output-names from dts.


address = to_output(hw)->address;
chan = &hmc->iio_channels[address];
if (!chan)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: this check looks redundant; you aren't likely going to get null here;
you could check if address is in range and return -EINVAL if it isn't, but from the looks of the code, it's likely that it's not going to happen

this also applies to other places in the code;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this. Thanks.

HMC7044 is a high performance, dual-loop, integer-N jitter attenuator
capable of performing reference selection and generation of ultralow
phase noise frequencies for high speed data converters with either
parallel or serial (JESD204B type) interfaces.

Signed-off-by: Dragos Bogdan <[email protected]>
HMC7044 is a high performance, dual-loop, integer-N jitter attenuator
capable of performing reference selection and generation of ultralow
phase noise frequencies for high speed data converters with either
parallel or serial (JESD204B type) interfaces.

Signed-off-by: Dragos Bogdan <[email protected]>
Copy link
Contributor

@commodo commodo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
Will merge later today if no objections

@commodo commodo merged commit 00cccff into altera_4.9 Jul 6, 2018
@commodo commodo deleted the hmc7044 branch July 6, 2018 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants