mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:24:45 +01:00
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:
parent
d4f13bc9aa
commit
60e5448ddb
1 changed files with 120 additions and 2 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue