There are actually 4 configuration modes of clock source for AD719X devices. Either a crystal can be attached externally between MCLK1 and MCLK2 pins, or an external CMOS-compatible clock can drive the MCLK2 pin. The other 2 modes make use of the 4.92MHz internal clock, which can be made available on the MCLK2 pin. Rename mclk to ext_clk for clarity. Note that the fix tag is for the commit that moved the driver out of staging. Fixes: b581f748cce0 ("staging: iio: adc: ad7192: move out of staging") Signed-off-by: Alisa-Dariana Roman <alisa.roman@xxxxxxxxxx> --- drivers/iio/adc/ad7192.c | 153 ++++++++++++++++++++++++++++++--------- 1 file changed, 119 insertions(+), 34 deletions(-) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index f06cb7ac4b42..75b0724142b1 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -8,6 +8,7 @@ #include <linux/interrupt.h> #include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -202,7 +203,8 @@ struct ad7192_state { const struct ad7192_chip_info *chip_info; struct regulator *avdd; struct regulator *vref; - struct clk *mclk; + struct clk *ext_clk; + struct clk_hw int_clk_hw; u16 int_vref_mv; u32 aincom_mv; u32 fclk; @@ -398,27 +400,6 @@ static inline bool ad7192_valid_external_frequency(u32 freq) freq <= AD7192_EXT_FREQ_MHZ_MAX); } -static int ad7192_clock_select(struct ad7192_state *st) -{ - struct device *dev = &st->sd.spi->dev; - unsigned int clock_sel; - - clock_sel = AD7192_CLK_INT; - - /* use internal clock */ - if (!st->mclk) { - if (device_property_read_bool(dev, "adi,int-clock-output-enable")) - clock_sel = AD7192_CLK_INT_CO; - } else { - if (device_property_read_bool(dev, "adi,clock-xtal")) - clock_sel = AD7192_CLK_EXT_MCLK1_2; - else - clock_sel = AD7192_CLK_EXT_MCLK2; - } - - return clock_sel; -} - static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev) { struct ad7192_state *st = iio_priv(indio_dev); @@ -1194,6 +1175,96 @@ static void ad7192_reg_disable(void *reg) regulator_disable(reg); } +static const char *const ad7192_clock_names[] = { + "xtal", + "clk" +}; + +static struct ad7192_state *clk_hw_to_ad7192(struct clk_hw *hw) +{ + return container_of(hw, struct ad7192_state, int_clk_hw); +} + +static void ad7192_clk_disable_unprepare(void *clk) +{ + clk_disable_unprepare(clk); +} + +static unsigned long ad7192_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return AD7192_INT_FREQ_MHZ; +} + +static int ad7192_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad7192_state *st = clk_hw_to_ad7192(hw); + + return st->clock_sel == AD7192_CLK_INT_CO; +} + +static int ad7192_clk_prepare(struct clk_hw *hw) +{ + struct ad7192_state *st = clk_hw_to_ad7192(hw); + int ret; + + st->mode &= ~AD7192_MODE_CLKSRC_MASK; + st->mode |= AD7192_CLK_INT_CO; + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret) + return ret; + + st->clock_sel = AD7192_CLK_INT_CO; + + return 0; +} + +static void ad7192_clk_unprepare(struct clk_hw *hw) +{ + struct ad7192_state *st = clk_hw_to_ad7192(hw); + int ret; + + st->mode &= ~AD7192_MODE_CLKSRC_MASK; + st->mode |= AD7192_CLK_INT; + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret) + return; + + st->clock_sel = AD7192_CLK_INT; +} + +static const struct clk_ops ad7192_int_clk_ops = { + .recalc_rate = ad7192_clk_recalc_rate, + .is_enabled = ad7192_clk_output_is_enabled, + .prepare = ad7192_clk_prepare, + .unprepare = ad7192_clk_unprepare, +}; + +static int ad7192_register_clk_provider(struct iio_dev *indio_dev) +{ + struct ad7192_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct clk_init_data init = {}; + int ret; + + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; + + init.name = fwnode_get_name(fwnode); + init.ops = &ad7192_int_clk_ops; + + st->int_clk_hw.init = &init; + ret = devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + static int ad7192_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -1312,20 +1383,34 @@ static int ad7192_probe(struct spi_device *spi) st->fclk = AD7192_INT_FREQ_MHZ; - st->mclk = devm_clk_get_optional_enabled(dev, "mclk"); - if (IS_ERR(st->mclk)) - return PTR_ERR(st->mclk); + ret = device_property_match_property_string(dev, "clock-names", + ad7192_clock_names, + ARRAY_SIZE(ad7192_clock_names)); + if (ret < 0) { + st->clock_sel = AD7192_CLK_INT; + st->fclk = AD7192_INT_FREQ_MHZ; - st->clock_sel = ad7192_clock_select(st); + ret = ad7192_register_clk_provider(indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Registration of clock provider failed\n"); + } else { + st->clock_sel = AD7192_CLK_EXT_MCLK1_2 + ret; - if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || - st->clock_sel == AD7192_CLK_EXT_MCLK2) { - st->fclk = clk_get_rate(st->mclk); - if (!ad7192_valid_external_frequency(st->fclk)) { - dev_err(dev, - "External clock frequency out of bounds\n"); - return -EINVAL; - } + st->ext_clk = devm_clk_get_enabled(dev, ad7192_clock_names[ret]); + if (IS_ERR(st->ext_clk)) + return PTR_ERR(st->ext_clk); + + ret = devm_add_action_or_reset(dev, + ad7192_clk_disable_unprepare, + st->ext_clk); + if (ret) + return ret; + + st->fclk = clk_get_rate(st->ext_clk); + if (!ad7192_valid_external_frequency(st->fclk)) + return dev_err_probe(dev, -EINVAL, + "External clock frequency out of bounds\n"); } ret = ad7192_setup(indio_dev, dev); -- 2.34.1