diff mbox series

[1/2] staging: iio: adc: ad7192: Add clock for external clock reference

Message ID 20181206091052.7644-1-mircea.caprioru@analog.com (mailing list archive)
State New, archived
Headers show
Series [1/2] staging: iio: adc: ad7192: Add clock for external clock reference | expand

Commit Message

Mircea Caprioru Dec. 6, 2018, 9:10 a.m. UTC
This patch adds a clock to the state structure of ad7192 for getting the
external clock frequency. This modifications is in accordance with clock
framework dt bindings documentation.

Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>
---
 drivers/staging/iio/adc/ad7192.c | 74 +++++++++++++++++++++-----------
 drivers/staging/iio/adc/ad7192.h |  2 -
 2 files changed, 50 insertions(+), 26 deletions(-)

Comments

Jonathan Cameron Dec. 8, 2018, 3:29 p.m. UTC | #1
On Thu, 6 Dec 2018 11:10:51 +0200
Mircea Caprioru <mircea.caprioru@analog.com> wrote:

> This patch adds a clock to the state structure of ad7192 for getting the
> external clock frequency. This modifications is in accordance with clock
> framework dt bindings documentation.
> 
> Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>

+cc Rob and the clk list for advise on how to do the binding for this one.

It is basically 2 pins, you can put a clock in on one of them or connect
a crystal across them.  The driver has to set a register to say which is
the case.  

Current proposal is two optional clocks (fall back to internal oscillator)
but that doesn't seem to be commonly done, so I'm wondering if there
is a 'standard' way to handle this sort of thing.

Thanks,

Jonathan

> ---
>  drivers/staging/iio/adc/ad7192.c | 74 +++++++++++++++++++++-----------
>  drivers/staging/iio/adc/ad7192.h |  2 -
>  2 files changed, 50 insertions(+), 26 deletions(-)
> 
> diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
> index acdbc07fd259..8a4e6ede42b3 100644
> --- a/drivers/staging/iio/adc/ad7192.c
> +++ b/drivers/staging/iio/adc/ad7192.c
> @@ -6,6 +6,7 @@
>   * Licensed under the GPL-2.
>   */
>  
> +#include <linux/clk.h>
>  #include <linux/interrupt.h>
>  #include <linux/device.h>
>  #include <linux/kernel.h>
> @@ -156,8 +157,9 @@
>  struct ad7192_state {
>  	struct regulator		*avdd;
>  	struct regulator		*dvdd;
> +	struct clk			*mclk;
>  	u16				int_vref_mv;
> -	u32				mclk;
> +	u32				fclk;
>  	u32				f_order;
>  	u32				mode;
>  	u32				conf;
> @@ -165,6 +167,7 @@ struct ad7192_state {
>  	u8				gpocon;
>  	u8				devid;
>  	struct mutex			lock;	/* protect sensor state */
> +	u8				clock_sel;
>  
>  	struct ad_sigma_delta		sd;
>  };
> @@ -250,28 +253,8 @@ static int ad7192_setup(struct ad7192_state *st,
>  		dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n",
>  			 id);
>  
> -	switch (pdata->clock_source_sel) {
> -	case AD7192_CLK_INT:
> -	case AD7192_CLK_INT_CO:
> -		st->mclk = AD7192_INT_FREQ_MHZ;
> -		break;
> -	case AD7192_CLK_EXT_MCLK1_2:
> -	case AD7192_CLK_EXT_MCLK2:
> -		if (ad7192_valid_external_frequency(pdata->ext_clk_hz)) {
> -			st->mclk = pdata->ext_clk_hz;
> -			break;
> -		}
> -		dev_err(&st->sd.spi->dev, "Invalid frequency setting %u\n",
> -			pdata->ext_clk_hz);
> -		ret = -EINVAL;
> -		goto out;
> -	default:
> -		ret = -EINVAL;
> -		goto out;
> -	}
> -
>  	st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) |
> -		AD7192_MODE_CLKSRC(pdata->clock_source_sel) |
> +		AD7192_MODE_CLKSRC(st->clock_sel) |
>  		AD7192_MODE_RATE(480);
>  
>  	st->conf = AD7192_CONF_GAIN(0);
> @@ -499,7 +482,7 @@ static int ad7192_read_raw(struct iio_dev *indio_dev,
>  			*val -= 273 * ad7192_get_temp_scale(unipolar);
>  		return IIO_VAL_INT;
>  	case IIO_CHAN_INFO_SAMP_FREQ:
> -		*val = st->mclk /
> +		*val = st->fclk /
>  			(st->f_order * 1024 * AD7192_MODE_RATE(st->mode));
>  		return IIO_VAL_INT;
>  	}
> @@ -546,7 +529,7 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
>  			break;
>  		}
>  
> -		div = st->mclk / (val * st->f_order * 1024);
> +		div = st->fclk / (val * st->f_order * 1024);
>  		if (div < 1 || div > 1023) {
>  			ret = -EINVAL;
>  			break;
> @@ -625,6 +608,42 @@ static const struct iio_chan_spec ad7193_channels[] = {
>  	IIO_CHAN_SOFT_TIMESTAMP(14),
>  };
>  
> +static int ad7192_clock_select(struct spi_device *spi, struct ad7192_state *st)
> +{
> +	int ret;
> +
> +	st->clock_sel = AD7192_CLK_EXT_MCLK2;
> +	st->mclk = devm_clk_get(&spi->dev, "clk");
> +	if (IS_ERR(st->mclk)) {
> +		if (PTR_ERR(st->mclk) != -ENOENT)
> +			return PTR_ERR(st->mclk);
> +
> +		/* try xtal option */
> +		st->mclk = devm_clk_get(&spi->dev, "xtal");

I'm not seeing any other driver using clock naming to distinguish
between different clock options. 

Which does raise the question of how to do this? 

I think it's probably going to be a single clock as both the external
clock and the crystal would be connected to the same pins.


> +		st->clock_sel = AD7192_CLK_EXT_MCLK1_2;
> +		if (IS_ERR(st->mclk)) {
> +			if (PTR_ERR(st->mclk) != -ENOENT)
> +				return PTR_ERR(st->mclk);
> +
> +			/* use internal clock */
> +			st->clock_sel = AD7192_CLK_INT;
> +			st->fclk = AD7192_INT_FREQ_MHZ;
> +		}
> +	}
> +	if (st->clock_sel == AD7192_CLK_EXT_MCLK2 ||
> +	    st->clock_sel == AD7192_CLK_EXT_MCLK1_2) {
> +		ret = clk_prepare_enable(st->mclk);
> +		if (ret < 0)
> +			return ret;
> +
> +		st->fclk = clk_get_rate(st->mclk);
> +		if (!ad7192_valid_external_frequency(st->fclk))
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int ad7192_probe(struct spi_device *spi)
>  {
>  	const struct ad7192_platform_data *pdata = dev_get_platdata(&spi->dev);
> @@ -672,6 +691,10 @@ static int ad7192_probe(struct spi_device *spi)
>  		goto error_disable_avdd;
>  	}
>  
> +	ret = ad7192_clock_select(spi, st);
> +	if (ret < 0)
> +		goto error_clk_disable_unprepare;
> +
>  	voltage_uv = regulator_get_voltage(st->avdd);
>  
>  	if (pdata->vref_mv)
> @@ -720,6 +743,8 @@ static int ad7192_probe(struct spi_device *spi)
>  
>  error_remove_trigger:
>  	ad_sd_cleanup_buffer_and_trigger(indio_dev);
> +error_clk_disable_unprepare:
> +	clk_disable_unprepare(st->mclk);
>  error_disable_dvdd:
>  	regulator_disable(st->dvdd);
>  error_disable_avdd:
> @@ -735,6 +760,7 @@ static int ad7192_remove(struct spi_device *spi)
>  
>  	iio_device_unregister(indio_dev);
>  	ad_sd_cleanup_buffer_and_trigger(indio_dev);
> +	clk_disable_unprepare(st->mclk);
>  
>  	regulator_disable(st->dvdd);
>  	regulator_disable(st->avdd);
> diff --git a/drivers/staging/iio/adc/ad7192.h b/drivers/staging/iio/adc/ad7192.h
> index 7433a43c2611..3be3ee269ed5 100644
> --- a/drivers/staging/iio/adc/ad7192.h
> +++ b/drivers/staging/iio/adc/ad7192.h
> @@ -33,8 +33,6 @@
>  
>  struct ad7192_platform_data {
>  	u16		vref_mv;
> -	u8		clock_source_sel;
> -	u32		ext_clk_hz;
>  	bool		refin2_en;
>  	bool		rej60_en;
>  	bool		sinc3_en;
Stephen Boyd Dec. 14, 2018, 1:39 a.m. UTC | #2
Quoting Jonathan Cameron (2018-12-08 07:29:54)
> On Thu, 6 Dec 2018 11:10:51 +0200
> Mircea Caprioru <mircea.caprioru@analog.com> wrote:
> 
> > This patch adds a clock to the state structure of ad7192 for getting the
> > external clock frequency. This modifications is in accordance with clock
> > framework dt bindings documentation.
> > 
> > Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>
> 
> +cc Rob and the clk list for advise on how to do the binding for this one.
> 
> It is basically 2 pins, you can put a clock in on one of them or connect
> a crystal across them.  The driver has to set a register to say which is
> the case.  
> 
> Current proposal is two optional clocks (fall back to internal oscillator)
> but that doesn't seem to be commonly done, so I'm wondering if there
> is a 'standard' way to handle this sort of thing.
> 

I'm not sure I fully understand, but I think perhaps
assigned-clock-parents would work? Or does that not work because either
way some parent is assigned, either the crystal or the optional clk that
isn't a crystal?
Jonathan Cameron Dec. 16, 2018, 10:07 a.m. UTC | #3
Rob, Clk experts, questions for you below.

Jonathan


On Thu, 13 Dec 2018 17:39:22 -0800
Stephen Boyd <sboyd@kernel.org> wrote:

> Quoting Jonathan Cameron (2018-12-08 07:29:54)
> > On Thu, 6 Dec 2018 11:10:51 +0200
> > Mircea Caprioru <mircea.caprioru@analog.com> wrote:
> >   
> > > This patch adds a clock to the state structure of ad7192 for getting the
> > > external clock frequency. This modifications is in accordance with clock
> > > framework dt bindings documentation.
> > > 
> > > Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>  
> > 
> > +cc Rob and the clk list for advise on how to do the binding for this one.
> > 
> > It is basically 2 pins, you can put a clock in on one of them or connect
> > a crystal across them.  The driver has to set a register to say which is
> > the case.  
> > 
> > Current proposal is two optional clocks (fall back to internal oscillator)
> > but that doesn't seem to be commonly done, so I'm wondering if there
> > is a 'standard' way to handle this sort of thing.
> >   
> 
> I'm not sure I fully understand, but I think perhaps
> assigned-clock-parents would work? Or does that not work because either
> way some parent is assigned, either the crystal or the optional clk that
> isn't a crystal?
> 
My concern is they aren't really separate clock inputs.   They are just different
ways of providing the same fundamental clock.  So I think we may want to just
provide a single clock and have another dt binding to say what it is.

So lots of ways we could do it, but I'm not sure what the right one to
go with is!

Jonathan
Stephen Boyd Jan. 24, 2019, 10:41 p.m. UTC | #4
Quoting Jonathan Cameron (2018-12-16 02:07:41)
> Rob, Clk experts, questions for you below.
> 
> Jonathan
> 
> 
> On Thu, 13 Dec 2018 17:39:22 -0800
> Stephen Boyd <sboyd@kernel.org> wrote:
> 
> > Quoting Jonathan Cameron (2018-12-08 07:29:54)
> > > On Thu, 6 Dec 2018 11:10:51 +0200
> > > Mircea Caprioru <mircea.caprioru@analog.com> wrote:
> > >   
> > > > This patch adds a clock to the state structure of ad7192 for getting the
> > > > external clock frequency. This modifications is in accordance with clock
> > > > framework dt bindings documentation.
> > > > 
> > > > Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>  
> > > 
> > > +cc Rob and the clk list for advise on how to do the binding for this one.
> > > 
> > > It is basically 2 pins, you can put a clock in on one of them or connect
> > > a crystal across them.  The driver has to set a register to say which is
> > > the case.  
> > > 
> > > Current proposal is two optional clocks (fall back to internal oscillator)
> > > but that doesn't seem to be commonly done, so I'm wondering if there
> > > is a 'standard' way to handle this sort of thing.
> > >   
> > 
> > I'm not sure I fully understand, but I think perhaps
> > assigned-clock-parents would work? Or does that not work because either
> > way some parent is assigned, either the crystal or the optional clk that
> > isn't a crystal?
> > 
> My concern is they aren't really separate clock inputs.   They are just different
> ways of providing the same fundamental clock.  So I think we may want to just
> provide a single clock and have another dt binding to say what it is.
> 
> So lots of ways we could do it, but I'm not sure what the right one to
> go with is!
> 

Sorry for getting back to this so late. Is the datasheet public for this
device? If so, any link to it?

If it's two pins, and sometimes one pin is connected and other times two
pins are connected but a register needs to be set if the pins are
connected in one configuration or the other I would say your plan for a
DT property indicating how the pins are configured sounds good. Usually
the hardware can figure these things out itself so I find this sort of
odd, but if this is how it is then there's not much that we can do.

It sounds like there aren't two different clk inputs to the device.
Given that information specifying two optional clks is incorrect because
there is only one 'slot' is the external clk source.
Alexandru Ardelean Jan. 25, 2019, 8:16 a.m. UTC | #5
On Fri, Jan 25, 2019 at 12:41 AM Stephen Boyd <sboyd@kernel.org> wrote:
>
> Quoting Jonathan Cameron (2018-12-16 02:07:41)
> > Rob, Clk experts, questions for you below.
> >
> > Jonathan
> >
> >
> > On Thu, 13 Dec 2018 17:39:22 -0800
> > Stephen Boyd <sboyd@kernel.org> wrote:
> >
> > > Quoting Jonathan Cameron (2018-12-08 07:29:54)
> > > > On Thu, 6 Dec 2018 11:10:51 +0200
> > > > Mircea Caprioru <mircea.caprioru@analog.com> wrote:
> > > >
> > > > > This patch adds a clock to the state structure of ad7192 for getting the
> > > > > external clock frequency. This modifications is in accordance with clock
> > > > > framework dt bindings documentation.
> > > > >
> > > > > Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>
> > > >
> > > > +cc Rob and the clk list for advise on how to do the binding for this one.
> > > >
> > > > It is basically 2 pins, you can put a clock in on one of them or connect
> > > > a crystal across them.  The driver has to set a register to say which is
> > > > the case.
> > > >
> > > > Current proposal is two optional clocks (fall back to internal oscillator)
> > > > but that doesn't seem to be commonly done, so I'm wondering if there
> > > > is a 'standard' way to handle this sort of thing.
> > > >
> > >
> > > I'm not sure I fully understand, but I think perhaps
> > > assigned-clock-parents would work? Or does that not work because either
> > > way some parent is assigned, either the crystal or the optional clk that
> > > isn't a crystal?
> > >
> > My concern is they aren't really separate clock inputs.   They are just different
> > ways of providing the same fundamental clock.  So I think we may want to just
> > provide a single clock and have another dt binding to say what it is.
> >
> > So lots of ways we could do it, but I'm not sure what the right one to
> > go with is!
> >
>
> Sorry for getting back to this so late. Is the datasheet public for this
> device? If so, any link to it?
>

Hey,
Link is http://analog.com/ad7192 and that should resolve to the proper
page, where the datasheet is available.
[ General info: most [if not all] datasheets from Analog Devices can
be found by concatenating "http://analog.com/ + "<part name>" ]

But more directly, the link to the PDF is:
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7192.pdf
Page 10 provides some description of the pins, page 20 the mode
register for the clock, and page 32 a general description of the
clock.
If you search for MCLK1 or MCLK2 you should navigate pretty quick
through the doc.

The clock circuitry [the 2 pins] is common for all chips this driver
supports [AD7190/2/3/5].

Thanks
Alex

> If it's two pins, and sometimes one pin is connected and other times two
> pins are connected but a register needs to be set if the pins are
> connected in one configuration or the other I would say your plan for a
> DT property indicating how the pins are configured sounds good. Usually
> the hardware can figure these things out itself so I find this sort of
> odd, but if this is how it is then there's not much that we can do.
>
> It sounds like there aren't two different clk inputs to the device.
> Given that information specifying two optional clks is incorrect because
> there is only one 'slot' is the external clk source.
>
diff mbox series

Patch

diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index acdbc07fd259..8a4e6ede42b3 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -6,6 +6,7 @@ 
  * Licensed under the GPL-2.
  */
 
+#include <linux/clk.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
@@ -156,8 +157,9 @@ 
 struct ad7192_state {
 	struct regulator		*avdd;
 	struct regulator		*dvdd;
+	struct clk			*mclk;
 	u16				int_vref_mv;
-	u32				mclk;
+	u32				fclk;
 	u32				f_order;
 	u32				mode;
 	u32				conf;
@@ -165,6 +167,7 @@  struct ad7192_state {
 	u8				gpocon;
 	u8				devid;
 	struct mutex			lock;	/* protect sensor state */
+	u8				clock_sel;
 
 	struct ad_sigma_delta		sd;
 };
@@ -250,28 +253,8 @@  static int ad7192_setup(struct ad7192_state *st,
 		dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n",
 			 id);
 
-	switch (pdata->clock_source_sel) {
-	case AD7192_CLK_INT:
-	case AD7192_CLK_INT_CO:
-		st->mclk = AD7192_INT_FREQ_MHZ;
-		break;
-	case AD7192_CLK_EXT_MCLK1_2:
-	case AD7192_CLK_EXT_MCLK2:
-		if (ad7192_valid_external_frequency(pdata->ext_clk_hz)) {
-			st->mclk = pdata->ext_clk_hz;
-			break;
-		}
-		dev_err(&st->sd.spi->dev, "Invalid frequency setting %u\n",
-			pdata->ext_clk_hz);
-		ret = -EINVAL;
-		goto out;
-	default:
-		ret = -EINVAL;
-		goto out;
-	}
-
 	st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) |
-		AD7192_MODE_CLKSRC(pdata->clock_source_sel) |
+		AD7192_MODE_CLKSRC(st->clock_sel) |
 		AD7192_MODE_RATE(480);
 
 	st->conf = AD7192_CONF_GAIN(0);
@@ -499,7 +482,7 @@  static int ad7192_read_raw(struct iio_dev *indio_dev,
 			*val -= 273 * ad7192_get_temp_scale(unipolar);
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SAMP_FREQ:
-		*val = st->mclk /
+		*val = st->fclk /
 			(st->f_order * 1024 * AD7192_MODE_RATE(st->mode));
 		return IIO_VAL_INT;
 	}
@@ -546,7 +529,7 @@  static int ad7192_write_raw(struct iio_dev *indio_dev,
 			break;
 		}
 
-		div = st->mclk / (val * st->f_order * 1024);
+		div = st->fclk / (val * st->f_order * 1024);
 		if (div < 1 || div > 1023) {
 			ret = -EINVAL;
 			break;
@@ -625,6 +608,42 @@  static const struct iio_chan_spec ad7193_channels[] = {
 	IIO_CHAN_SOFT_TIMESTAMP(14),
 };
 
+static int ad7192_clock_select(struct spi_device *spi, struct ad7192_state *st)
+{
+	int ret;
+
+	st->clock_sel = AD7192_CLK_EXT_MCLK2;
+	st->mclk = devm_clk_get(&spi->dev, "clk");
+	if (IS_ERR(st->mclk)) {
+		if (PTR_ERR(st->mclk) != -ENOENT)
+			return PTR_ERR(st->mclk);
+
+		/* try xtal option */
+		st->mclk = devm_clk_get(&spi->dev, "xtal");
+		st->clock_sel = AD7192_CLK_EXT_MCLK1_2;
+		if (IS_ERR(st->mclk)) {
+			if (PTR_ERR(st->mclk) != -ENOENT)
+				return PTR_ERR(st->mclk);
+
+			/* use internal clock */
+			st->clock_sel = AD7192_CLK_INT;
+			st->fclk = AD7192_INT_FREQ_MHZ;
+		}
+	}
+	if (st->clock_sel == AD7192_CLK_EXT_MCLK2 ||
+	    st->clock_sel == AD7192_CLK_EXT_MCLK1_2) {
+		ret = clk_prepare_enable(st->mclk);
+		if (ret < 0)
+			return ret;
+
+		st->fclk = clk_get_rate(st->mclk);
+		if (!ad7192_valid_external_frequency(st->fclk))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int ad7192_probe(struct spi_device *spi)
 {
 	const struct ad7192_platform_data *pdata = dev_get_platdata(&spi->dev);
@@ -672,6 +691,10 @@  static int ad7192_probe(struct spi_device *spi)
 		goto error_disable_avdd;
 	}
 
+	ret = ad7192_clock_select(spi, st);
+	if (ret < 0)
+		goto error_clk_disable_unprepare;
+
 	voltage_uv = regulator_get_voltage(st->avdd);
 
 	if (pdata->vref_mv)
@@ -720,6 +743,8 @@  static int ad7192_probe(struct spi_device *spi)
 
 error_remove_trigger:
 	ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_clk_disable_unprepare:
+	clk_disable_unprepare(st->mclk);
 error_disable_dvdd:
 	regulator_disable(st->dvdd);
 error_disable_avdd:
@@ -735,6 +760,7 @@  static int ad7192_remove(struct spi_device *spi)
 
 	iio_device_unregister(indio_dev);
 	ad_sd_cleanup_buffer_and_trigger(indio_dev);
+	clk_disable_unprepare(st->mclk);
 
 	regulator_disable(st->dvdd);
 	regulator_disable(st->avdd);
diff --git a/drivers/staging/iio/adc/ad7192.h b/drivers/staging/iio/adc/ad7192.h
index 7433a43c2611..3be3ee269ed5 100644
--- a/drivers/staging/iio/adc/ad7192.h
+++ b/drivers/staging/iio/adc/ad7192.h
@@ -33,8 +33,6 @@ 
 
 struct ad7192_platform_data {
 	u16		vref_mv;
-	u8		clock_source_sel;
-	u32		ext_clk_hz;
 	bool		refin2_en;
 	bool		rej60_en;
 	bool		sinc3_en;