Re: [PATCH v2 1/2] iio: imu: adis16480: Add support for external clock

2019-03-16 Thread Jonathan Cameron
On Mon, 11 Mar 2019 11:45:29 +0200
Stefan Popa  wrote:

> Inertial sensor data collection and processing can be controlled by
> configuring one of the DIOx lines as an external clock input. This
> option is available for all devices supported by this driver. However,
> only adis1649x devices support different modes for the external clock.
> 
> Sync mode is supported by all devices. In this mode, the output data
> rate is equal with the clock frequency divided by DEC_RATE + 1. This
> mode of calculation is similar with the case when the internal clock is
> used.
> 
> Pulse Per Second (PPS) Mode, is only supported by adis1649x devices. In
> this mode, the output data rate is equal to the product of the external
> clock frequency and the scale factor in the SYNC_SCALE register.
> 
> This patch uses the "clock-names" property to enable the external clock
> in one of the two supported modes: "sync" or "pps". This property is
> optional. If it is not specified, the internal clock is used.
> 
> This patch also offers the option to select the DIOx line to be used as
> an external clock input via the custom "adi,ext-clk-pin" property. If this
> field is left empty, DIO2 is assigned as default external clock input pin.
> Each DIOx pin supports only one function at a time (data ready line
> selection or external clock input).
> 
> Signed-off-by: Stefan Popa 
Applied to the togreg branch of iio.git and pushed out as testing for
the autobuilders to play with it.

Thanks,

Jonathan

> ---
> Changes in v2:
>   - used ADIS16480_DRDY_SEL() macro when checking for external clock
> input pin.
> 
>  drivers/iio/imu/adis16480.c | 186 
> ++--
>  1 file changed, 179 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
> index 28cece3..ab137c1 100644
> --- a/drivers/iio/imu/adis16480.c
> +++ b/drivers/iio/imu/adis16480.c
> @@ -9,6 +9,7 @@
>   *
>   */
>  
> +#include 
>  #include 
>  #include 
>  #include 
> @@ -99,6 +100,12 @@
>  #define ADIS16480_REG_FIRM_DMADIS16480_REG(0x03, 
> 0x7A)
>  #define ADIS16480_REG_FIRM_Y ADIS16480_REG(0x03, 0x7C)
>  
> +/*
> + * External clock scaling in PPS mode.
> + * Available only for ADIS1649x devices
> + */
> +#define ADIS16495_REG_SYNC_SCALE ADIS16480_REG(0x03, 0x10)
> +
>  #define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20)
>  
>  /* Each filter coefficent bank spans two pages */
> @@ -116,6 +123,12 @@
>  #define ADIS16480_DRDY_POL(x)
> FIELD_PREP(ADIS16480_DRDY_POL_MSK, x)
>  #define ADIS16480_DRDY_EN_MSKBIT(3)
>  #define ADIS16480_DRDY_EN(x) FIELD_PREP(ADIS16480_DRDY_EN_MSK, x)
> +#define ADIS16480_SYNC_SEL_MSK   GENMASK(5, 4)
> +#define ADIS16480_SYNC_SEL(x)
> FIELD_PREP(ADIS16480_SYNC_SEL_MSK, x)
> +#define ADIS16480_SYNC_EN_MSKBIT(7)
> +#define ADIS16480_SYNC_EN(x) FIELD_PREP(ADIS16480_SYNC_EN_MSK, x)
> +#define ADIS16480_SYNC_MODE_MSK  BIT(8)
> +#define ADIS16480_SYNC_MODE(x)   
> FIELD_PREP(ADIS16480_SYNC_MODE_MSK, x)
>  
>  struct adis16480_chip_info {
>   unsigned int num_channels;
> @@ -128,6 +141,7 @@ struct adis16480_chip_info {
>   unsigned int int_clk;
>   unsigned int max_dec_rate;
>   const unsigned int *filter_freqs;
> + bool has_pps_clk_mode;
>  };
>  
>  enum adis16480_int_pin {
> @@ -137,10 +151,19 @@ enum adis16480_int_pin {
>   ADIS16480_PIN_DIO4
>  };
>  
> +enum adis16480_clock_mode {
> + ADIS16480_CLK_SYNC,
> + ADIS16480_CLK_PPS,
> + ADIS16480_CLK_INT
> +};
> +
>  struct adis16480 {
>   const struct adis16480_chip_info *chip_info;
>  
>   struct adis adis;
> + struct clk *ext_clk;
> + enum adis16480_clock_mode clk_mode;
> + unsigned int clk_freq;
>  };
>  
>  static const char * const adis16480_int_pin_names[4] = {
> @@ -296,20 +319,34 @@ static int adis16480_debugfs_init(struct iio_dev 
> *indio_dev)
>  static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2)
>  {
>   struct adis16480 *st = iio_priv(indio_dev);
> - unsigned int t;
> + unsigned int t, reg;
>  
>   t =  val * 1000 + val2 / 1000;
>   if (t <= 0)
>   return -EINVAL;
>  
> - t = st->chip_info->int_clk / t;
> + /*
> +  * When using PPS mode, the rate of data collection is equal to the
> +  * product of the external clock frequency and the scale factor in the
> +  * SYNC_SCALE register.
> +  * When using sync mode, or internal clock, the output data rate is
> +  * equal with  the clock frequency divided by DEC_RATE + 1.
> +  */
> + if (st->clk_mode == ADIS16480_CLK_PPS) {
> + t = t / st->clk_freq;
> + reg = ADIS16495_REG_SYNC_SCALE;
> + } else {
> + t = st->clk_freq / t;
> + reg = ADIS16480_REG_DEC_RATE;
> + }
> +
>   

[PATCH v2 1/2] iio: imu: adis16480: Add support for external clock

2019-03-11 Thread Stefan Popa
Inertial sensor data collection and processing can be controlled by
configuring one of the DIOx lines as an external clock input. This
option is available for all devices supported by this driver. However,
only adis1649x devices support different modes for the external clock.

Sync mode is supported by all devices. In this mode, the output data
rate is equal with the clock frequency divided by DEC_RATE + 1. This
mode of calculation is similar with the case when the internal clock is
used.

Pulse Per Second (PPS) Mode, is only supported by adis1649x devices. In
this mode, the output data rate is equal to the product of the external
clock frequency and the scale factor in the SYNC_SCALE register.

This patch uses the "clock-names" property to enable the external clock
in one of the two supported modes: "sync" or "pps". This property is
optional. If it is not specified, the internal clock is used.

This patch also offers the option to select the DIOx line to be used as
an external clock input via the custom "adi,ext-clk-pin" property. If this
field is left empty, DIO2 is assigned as default external clock input pin.
Each DIOx pin supports only one function at a time (data ready line
selection or external clock input).

Signed-off-by: Stefan Popa 
---
Changes in v2:
- used ADIS16480_DRDY_SEL() macro when checking for external clock
  input pin.

 drivers/iio/imu/adis16480.c | 186 ++--
 1 file changed, 179 insertions(+), 7 deletions(-)

diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index 28cece3..ab137c1 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -9,6 +9,7 @@
  *
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -99,6 +100,12 @@
 #define ADIS16480_REG_FIRM_DM  ADIS16480_REG(0x03, 0x7A)
 #define ADIS16480_REG_FIRM_Y   ADIS16480_REG(0x03, 0x7C)
 
+/*
+ * External clock scaling in PPS mode.
+ * Available only for ADIS1649x devices
+ */
+#define ADIS16495_REG_SYNC_SCALE   ADIS16480_REG(0x03, 0x10)
+
 #define ADIS16480_REG_SERIAL_NUM   ADIS16480_REG(0x04, 0x20)
 
 /* Each filter coefficent bank spans two pages */
@@ -116,6 +123,12 @@
 #define ADIS16480_DRDY_POL(x)  FIELD_PREP(ADIS16480_DRDY_POL_MSK, x)
 #define ADIS16480_DRDY_EN_MSK  BIT(3)
 #define ADIS16480_DRDY_EN(x)   FIELD_PREP(ADIS16480_DRDY_EN_MSK, x)
+#define ADIS16480_SYNC_SEL_MSK GENMASK(5, 4)
+#define ADIS16480_SYNC_SEL(x)  FIELD_PREP(ADIS16480_SYNC_SEL_MSK, x)
+#define ADIS16480_SYNC_EN_MSK  BIT(7)
+#define ADIS16480_SYNC_EN(x)   FIELD_PREP(ADIS16480_SYNC_EN_MSK, x)
+#define ADIS16480_SYNC_MODE_MSKBIT(8)
+#define ADIS16480_SYNC_MODE(x) FIELD_PREP(ADIS16480_SYNC_MODE_MSK, x)
 
 struct adis16480_chip_info {
unsigned int num_channels;
@@ -128,6 +141,7 @@ struct adis16480_chip_info {
unsigned int int_clk;
unsigned int max_dec_rate;
const unsigned int *filter_freqs;
+   bool has_pps_clk_mode;
 };
 
 enum adis16480_int_pin {
@@ -137,10 +151,19 @@ enum adis16480_int_pin {
ADIS16480_PIN_DIO4
 };
 
+enum adis16480_clock_mode {
+   ADIS16480_CLK_SYNC,
+   ADIS16480_CLK_PPS,
+   ADIS16480_CLK_INT
+};
+
 struct adis16480 {
const struct adis16480_chip_info *chip_info;
 
struct adis adis;
+   struct clk *ext_clk;
+   enum adis16480_clock_mode clk_mode;
+   unsigned int clk_freq;
 };
 
 static const char * const adis16480_int_pin_names[4] = {
@@ -296,20 +319,34 @@ static int adis16480_debugfs_init(struct iio_dev 
*indio_dev)
 static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2)
 {
struct adis16480 *st = iio_priv(indio_dev);
-   unsigned int t;
+   unsigned int t, reg;
 
t =  val * 1000 + val2 / 1000;
if (t <= 0)
return -EINVAL;
 
-   t = st->chip_info->int_clk / t;
+   /*
+* When using PPS mode, the rate of data collection is equal to the
+* product of the external clock frequency and the scale factor in the
+* SYNC_SCALE register.
+* When using sync mode, or internal clock, the output data rate is
+* equal with  the clock frequency divided by DEC_RATE + 1.
+*/
+   if (st->clk_mode == ADIS16480_CLK_PPS) {
+   t = t / st->clk_freq;
+   reg = ADIS16495_REG_SYNC_SCALE;
+   } else {
+   t = st->clk_freq / t;
+   reg = ADIS16480_REG_DEC_RATE;
+   }
+
if (t > st->chip_info->max_dec_rate)
t = st->chip_info->max_dec_rate;
 
-   if (t != 0)
+   if ((t != 0) && (st->clk_mode != ADIS16480_CLK_PPS))
t--;
 
-   return adis_write_reg_16(>adis, ADIS16480_REG_DEC_RATE, t);
+   return adis_write_reg_16(>adis, reg, t);
 }
 
 static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2)
@@ -318,12