Commit 39c024b5 authored by Nuno Sa's avatar Nuno Sa Committed by Jonathan Cameron

iio: adis16475: improve sync scale mode handling

With this patch, we don't force users to define the IMU scaled internal
sampling rate once in the devicetree. Now it's dynamically calculated
at runtime depending on the desired output rate given by users.

Calculating the sync_scale dynamically gives us better chances of
achieving a perfect/integer value for DEC_RATE (thus giving more
flexibility). The math is:
 1. lcm of the input clock and the desired output rate.
 2. get the highest multiple of the previous result lower than the adis
    max rate.
 3. The last result becomes the IMU sample rate. Use that to calculate
    SYNC_SCALE and DEC_RATE (to get the user output rate).
Signed-off-by: default avatarNuno Sa <nuno.sa@analog.com>
Link: https://lore.kernel.org/r/20210218114039.216091-3-nuno.sa@analog.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 0463e60f
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <linux/iio/sysfs.h> #include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h> #include <linux/iio/trigger_consumer.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/lcm.h>
#include <linux/math.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/property.h> #include <linux/property.h>
...@@ -101,6 +103,7 @@ struct adis16475 { ...@@ -101,6 +103,7 @@ struct adis16475 {
u32 clk_freq; u32 clk_freq;
bool burst32; bool burst32;
unsigned long lsb_flag; unsigned long lsb_flag;
u16 sync_mode;
/* Alignment needed for the timestamp */ /* Alignment needed for the timestamp */
__be16 data[ADIS16475_MAX_SCAN_DATA] __aligned(8); __be16 data[ADIS16475_MAX_SCAN_DATA] __aligned(8);
}; };
...@@ -117,6 +120,11 @@ enum { ...@@ -117,6 +120,11 @@ enum {
ADIS16475_SCAN_CRC_FAILURE, ADIS16475_SCAN_CRC_FAILURE,
}; };
static bool low_rate_allow;
module_param(low_rate_allow, bool, 0444);
MODULE_PARM_DESC(low_rate_allow,
"Allow IMU rates below the minimum advisable when external clk is used in SCALED mode (default: N)");
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
static ssize_t adis16475_show_firmware_revision(struct file *file, static ssize_t adis16475_show_firmware_revision(struct file *file,
char __user *userbuf, char __user *userbuf,
...@@ -253,25 +261,92 @@ static int adis16475_get_freq(struct adis16475 *st, u32 *freq) ...@@ -253,25 +261,92 @@ static int adis16475_get_freq(struct adis16475 *st, u32 *freq)
{ {
int ret; int ret;
u16 dec; u16 dec;
u32 sample_rate = st->clk_freq;
mutex_lock(&st->adis.state_lock);
if (st->sync_mode == ADIS16475_SYNC_SCALED) {
u16 sync_scale;
ret = __adis_read_reg_16(&st->adis, ADIS16475_REG_UP_SCALE, &sync_scale);
if (ret)
goto error;
sample_rate = st->clk_freq * sync_scale;
}
ret = adis_read_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, &dec); ret = __adis_read_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, &dec);
if (ret) if (ret)
return -EINVAL; goto error;
*freq = DIV_ROUND_CLOSEST(st->clk_freq, dec + 1); mutex_unlock(&st->adis.state_lock);
*freq = DIV_ROUND_CLOSEST(sample_rate, dec + 1);
return 0; return 0;
error:
mutex_unlock(&st->adis.state_lock);
return ret;
} }
static int adis16475_set_freq(struct adis16475 *st, const u32 freq) static int adis16475_set_freq(struct adis16475 *st, const u32 freq)
{ {
u16 dec; u16 dec;
int ret; int ret;
u32 sample_rate = st->clk_freq;
if (!freq) if (!freq)
return -EINVAL; return -EINVAL;
dec = DIV_ROUND_CLOSEST(st->clk_freq, freq); mutex_lock(&st->adis.state_lock);
/*
* When using sync scaled mode, the input clock needs to be scaled so that we have
* an IMU sample rate between (optimally) 1900 and 2100. After this, we can use the
* decimation filter to lower the sampling rate in order to get what the user wants.
* Optimally, the user sample rate is a multiple of both the IMU sample rate and
* the input clock. Hence, calculating the sync_scale dynamically gives us better
* chances of achieving a perfect/integer value for DEC_RATE. The math here is:
* 1. lcm of the input clock and the desired output rate.
* 2. get the highest multiple of the previous result lower than the adis max rate.
* 3. The last result becomes the IMU sample rate. Use that to calculate SYNC_SCALE
* and DEC_RATE (to get the user output rate)
*/
if (st->sync_mode == ADIS16475_SYNC_SCALED) {
unsigned long scaled_rate = lcm(st->clk_freq, freq);
int sync_scale;
/*
* If lcm is bigger than the IMU maximum sampling rate there's no perfect
* solution. In this case, we get the highest multiple of the input clock
* lower than the IMU max sample rate.
*/
if (scaled_rate > 2100000)
scaled_rate = 2100000 / st->clk_freq * st->clk_freq;
else
scaled_rate = 2100000 / scaled_rate * scaled_rate;
/*
* This is not an hard requirement but it's not advised to run the IMU
* with a sample rate lower than 4000Hz due to possible undersampling
* issues. However, there are users that might really want to take the risk.
* Hence, we provide a module parameter for them. If set, we allow sample
* rates lower than 4KHz. By default, we won't allow this and we just roundup
* the rate to the next multiple of the input clock bigger than 4KHz. This
* is done like this as in some cases (when DEC_RATE is 0) might give
* us the closest value to the one desired by the user...
*/
if (scaled_rate < 1900000 && !low_rate_allow)
scaled_rate = roundup(1900000, st->clk_freq);
sync_scale = scaled_rate / st->clk_freq;
ret = __adis_write_reg_16(&st->adis, ADIS16475_REG_UP_SCALE, sync_scale);
if (ret)
goto error;
sample_rate = scaled_rate;
}
dec = DIV_ROUND_CLOSEST(sample_rate, freq);
if (dec) if (dec)
dec--; dec--;
...@@ -281,7 +356,7 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) ...@@ -281,7 +356,7 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq)
ret = adis_write_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, dec); ret = adis_write_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, dec);
if (ret) if (ret)
return ret; goto error;
/* /*
* If decimation is used, then gyro and accel data will have meaningful * If decimation is used, then gyro and accel data will have meaningful
...@@ -290,6 +365,9 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) ...@@ -290,6 +365,9 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq)
assign_bit(ADIS16475_LSB_DEC_MASK, &st->lsb_flag, dec); assign_bit(ADIS16475_LSB_DEC_MASK, &st->lsb_flag, dec);
return 0; return 0;
error:
mutex_unlock(&st->adis.state_lock);
return ret;
} }
/* The values are approximated. */ /* The values are approximated. */
...@@ -1085,6 +1163,7 @@ static int adis16475_config_sync_mode(struct adis16475 *st) ...@@ -1085,6 +1163,7 @@ static int adis16475_config_sync_mode(struct adis16475 *st)
} }
sync = &st->info->sync[sync_mode]; sync = &st->info->sync[sync_mode];
st->sync_mode = sync->sync_mode;
/* All the other modes require external input signal */ /* All the other modes require external input signal */
if (sync->sync_mode != ADIS16475_SYNC_OUTPUT) { if (sync->sync_mode != ADIS16475_SYNC_OUTPUT) {
...@@ -1112,37 +1191,20 @@ static int adis16475_config_sync_mode(struct adis16475 *st) ...@@ -1112,37 +1191,20 @@ static int adis16475_config_sync_mode(struct adis16475 *st)
if (sync->sync_mode == ADIS16475_SYNC_SCALED) { if (sync->sync_mode == ADIS16475_SYNC_SCALED) {
u16 up_scale; u16 up_scale;
u32 scaled_out_freq = 0;
/* /*
* If we are in scaled mode, we must have an up_scale. * In sync scaled mode, the IMU sample rate is the clk_freq * sync_scale.
* In scaled mode the allowable input clock range is * Hence, default the IMU sample rate to the highest multiple of the input
* 1 Hz to 128 Hz, and the allowable output range is * clock lower than the IMU max sample rate. The optimal range is
* 1900 to 2100 Hz. Hence, a scale must be given to * 1900-2100 sps...
* get the allowable output.
*/ */
ret = device_property_read_u32(dev, up_scale = 2100 / st->clk_freq;
"adi,scaled-output-hz",
&scaled_out_freq);
if (ret) {
dev_err(dev, "adi,scaled-output-hz must be given when in scaled sync mode");
return -EINVAL;
} else if (scaled_out_freq < 1900 ||
scaled_out_freq > 2100) {
dev_err(dev, "Invalid value: %u for adi,scaled-output-hz",
scaled_out_freq);
return -EINVAL;
}
up_scale = DIV_ROUND_CLOSEST(scaled_out_freq,
st->clk_freq);
ret = __adis_write_reg_16(&st->adis, ret = __adis_write_reg_16(&st->adis,
ADIS16475_REG_UP_SCALE, ADIS16475_REG_UP_SCALE,
up_scale); up_scale);
if (ret) if (ret)
return ret; return ret;
st->clk_freq = scaled_out_freq;
} }
st->clk_freq *= 1000; st->clk_freq *= 1000;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment