iio: frequency: adf4377: add clk provider support

Add clk provider feature for the adf4377.

Even though the driver was sent as an IIO driver in most cases the
device is actually seen as a clock provider.

This patch aims to cover actual usecases requested by users in order to
completely control the output frequencies from userspace.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Antoniu Miclaus 2025-12-12 16:47:32 +02:00 committed by Jonathan Cameron
parent d4f13bc9aa
commit 60e5448ddb

View file

@ -8,7 +8,9 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/container_of.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
@ -435,9 +437,14 @@ struct adf4377_state {
struct gpio_desc *gpio_ce;
struct gpio_desc *gpio_enclk1;
struct gpio_desc *gpio_enclk2;
struct clk *clk;
struct clk *clkout;
struct clk_hw hw;
u8 buf[2] __aligned(IIO_DMA_MINALIGN);
};
#define to_adf4377_state(h) container_of(h, struct adf4377_state, hw)
static const char * const adf4377_muxout_modes[] = {
[ADF4377_MUXOUT_HIGH_Z] = "high_z",
[ADF4377_MUXOUT_LKDET] = "lock_detect",
@ -929,6 +936,110 @@ static int adf4377_freq_change(struct notifier_block *nb, unsigned long action,
return NOTIFY_OK;
}
static unsigned long adf4377_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct adf4377_state *st = to_adf4377_state(hw);
u64 freq;
int ret;
ret = adf4377_get_freq(st, &freq);
if (ret)
return 0;
return freq;
}
static int adf4377_clk_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct adf4377_state *st = to_adf4377_state(hw);
return adf4377_set_freq(st, rate);
}
static int adf4377_clk_prepare(struct clk_hw *hw)
{
struct adf4377_state *st = to_adf4377_state(hw);
return regmap_update_bits(st->regmap, 0x1a, ADF4377_001A_PD_CLKOUT1_MSK |
ADF4377_001A_PD_CLKOUT2_MSK,
FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 0) |
FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 0));
}
static void adf4377_clk_unprepare(struct clk_hw *hw)
{
struct adf4377_state *st = to_adf4377_state(hw);
regmap_update_bits(st->regmap, 0x1a, ADF4377_001A_PD_CLKOUT1_MSK |
ADF4377_001A_PD_CLKOUT2_MSK,
FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 1) |
FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 1));
}
static int adf4377_clk_is_prepared(struct clk_hw *hw)
{
struct adf4377_state *st = to_adf4377_state(hw);
unsigned int readval;
int ret;
ret = regmap_read(st->regmap, 0x1a, &readval);
if (ret)
return ret;
return !(readval & (ADF4377_001A_PD_CLKOUT1_MSK | ADF4377_001A_PD_CLKOUT2_MSK));
}
static const struct clk_ops adf4377_clk_ops = {
.recalc_rate = adf4377_clk_recalc_rate,
.set_rate = adf4377_clk_set_rate,
.prepare = adf4377_clk_prepare,
.unprepare = adf4377_clk_unprepare,
.is_prepared = adf4377_clk_is_prepared,
};
static int adf4377_clk_register(struct adf4377_state *st)
{
struct spi_device *spi = st->spi;
struct device *dev = &spi->dev;
struct clk_init_data init;
struct clk_parent_data parent_data;
int ret;
if (!device_property_present(dev, "#clock-cells"))
return 0;
ret = device_property_read_string(dev, "clock-output-names", &init.name);
if (ret) {
init.name = devm_kasprintf(dev, GFP_KERNEL, "%pfw-clk",
dev_fwnode(dev));
if (!init.name)
return -ENOMEM;
}
parent_data.fw_name = "ref_in";
init.ops = &adf4377_clk_ops;
init.parent_data = &parent_data;
init.num_parents = 1;
init.flags = CLK_SET_RATE_PARENT;
st->hw.init = &init;
ret = devm_clk_hw_register(dev, &st->hw);
if (ret)
return ret;
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &st->hw);
if (ret)
return ret;
st->clkout = st->hw.clk;
return 0;
}
static const struct adf4377_chip_info adf4377_chip_info = {
.name = "adf4377",
.has_gpio_enclk2 = true,
@ -958,8 +1069,6 @@ static int adf4377_probe(struct spi_device *spi)
indio_dev->info = &adf4377_info;
indio_dev->name = "adf4377";
indio_dev->channels = adf4377_channels;
indio_dev->num_channels = ARRAY_SIZE(adf4377_channels);
st->regmap = regmap;
st->spi = spi;
@ -979,6 +1088,15 @@ static int adf4377_probe(struct spi_device *spi)
if (ret)
return ret;
ret = adf4377_clk_register(st);
if (ret)
return ret;
if (!st->clkout) {
indio_dev->channels = adf4377_channels;
indio_dev->num_channels = ARRAY_SIZE(adf4377_channels);
}
return devm_iio_device_register(&spi->dev, indio_dev);
}