-
Notifications
You must be signed in to change notification settings - Fork 836
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
Conversation
There was a problem hiding this 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.
drivers/iio/frequency/hmc7044.c
Outdated
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); |
There was a problem hiding this comment.
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.
drivers/iio/frequency/hmc7044.c
Outdated
|
||
#define HMC7044_LOW_VCO_MIN 2150000 | ||
#define HMC7044_LOW_VCO_MAX 2880000 | ||
#define HMC7044_HIGH_VCO_MIN 2400000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
datasheet has 2650 here.
drivers/iio/frequency/hmc7044.c
Outdated
int div; | ||
|
||
div = DIV_ROUND_CLOSEST(hmc->pll2_freq, rate); | ||
div = clamp(div, 1, 4094); |
There was a problem hiding this comment.
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.
drivers/iio/frequency/hmc7044.c
Outdated
hmc->clk_data.clks = hmc->clks; | ||
hmc->clk_data.clk_num = HMC7044_NUM_CHAN; | ||
|
||
ret = of_clk_add_provider(hmc->spi->dev.of_node, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just return of_clk...
drivers/iio/frequency/hmc7044.c
Outdated
} | ||
|
||
return -EINVAL; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra newline
drivers/iio/frequency/hmc7044.c
Outdated
int i; | ||
|
||
for (i = 0; i < hmc->num_channels; i++) { | ||
if (hmc->channels[i].num == chan->channel) |
There was a problem hiding this comment.
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?
drivers/iio/frequency/hmc7044.c
Outdated
hmc->iio_channels[i].info_mask_separate = | ||
BIT(IIO_CHAN_INFO_FREQUENCY); | ||
|
||
ret = hmc7044_clk_register(indio_dev, chan->num); |
There was a problem hiding this comment.
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.
drivers/iio/frequency/hmc7044.c
Outdated
if (ret) | ||
return ret; | ||
|
||
ret = iio_device_register(indio_dev); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return iio_device_register()
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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;
There was a problem hiding this comment.
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()
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is what? ;)
drivers/iio/frequency/hmc7044.c
Outdated
buf[1] = cmd & 0xFF; | ||
buf[2] = val; | ||
|
||
ret = spi_write(hmc->spi, buf, 3); |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed this.
There was a problem hiding this 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
drivers/iio/frequency/hmc7044.c
Outdated
* Copyright 2018 Analog Devices Inc. | ||
* | ||
* Licensed under the GPL-2. | ||
*/ |
There was a problem hiding this comment.
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
drivers/iio/frequency/hmc7044.c
Outdated
u32 clkin_freq[4]; | ||
u32 vcxo_freq; | ||
u32 pll2_freq; | ||
unsigned int pll1_loop_bw; |
There was a problem hiding this comment.
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;
There was a problem hiding this comment.
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.
drivers/iio/frequency/hmc7044.c
Outdated
|
||
static int hmc7044_write(struct iio_dev *indio_dev, | ||
unsigned int reg, | ||
unsigned int val) |
There was a problem hiding this comment.
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
drivers/iio/frequency/hmc7044.c
Outdated
else | ||
high_vco_en = false; | ||
|
||
|
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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 {
...
}
drivers/iio/frequency/hmc7044.c
Outdated
if (ret) | ||
return ret; | ||
|
||
ret = iio_device_register(indio_dev); |
There was a problem hiding this comment.
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()
?
drivers/iio/frequency/hmc7044.c
Outdated
*/ | ||
#include <linux/module.h> | ||
#include <linux/device.h> | ||
#include <linux/kernel.h> |
There was a problem hiding this comment.
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>
9197e8a
to
3e01e56
Compare
v2:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
drivers/iio/frequency/hmc7044.c
Outdated
|
||
div = DIV_ROUND_CLOSEST(parent_rate, rate); | ||
/* Supported odd divide ratios are 1, 3, and 5 */ | ||
if ((div != 1) && (div != 3) && (div != 5)) { |
There was a problem hiding this comment.
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.
drivers/iio/frequency/hmc7044.c
Outdated
char name[SPI_NAME_SIZE + 8]; | ||
|
||
sprintf(name, "%s_out%d", indio_dev->name, num); | ||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
drivers/iio/frequency/hmc7044.c
Outdated
#define HMC7044_OUT_DIV_MIN 1 | ||
#define HMC7044_OUT_DIV_MAX 4094 | ||
|
||
struct output { |
There was a problem hiding this comment.
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.
dbdbf65
to
bacf9c4
Compare
v3:
|
drivers/iio/frequency/hmc7044.c
Outdated
|
||
address = to_output(hw)->address; | ||
chan = &hmc->iio_channels[address]; | ||
if (!chan) |
There was a problem hiding this comment.
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;
There was a problem hiding this comment.
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]>
There was a problem hiding this 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
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]