summaryrefslogtreecommitdiff
path: root/drivers/iio/adc/vf610_adc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc/vf610_adc.c')
-rw-r--r--drivers/iio/adc/vf610_adc.c228
1 files changed, 181 insertions, 47 deletions
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index 56292ae4538d..f4df2a709f05 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -68,6 +68,9 @@
#define VF610_ADC_CLK_DIV8 0x60
#define VF610_ADC_CLK_MASK 0x60
#define VF610_ADC_ADLSMP_LONG 0x10
+#define VF610_ADC_ADSTS_SHORT 0x100
+#define VF610_ADC_ADSTS_NORMAL 0x200
+#define VF610_ADC_ADSTS_LONG 0x300
#define VF610_ADC_ADSTS_MASK 0x300
#define VF610_ADC_ADLPC_EN 0x80
#define VF610_ADC_ADHSC_EN 0x400
@@ -98,6 +101,8 @@
#define VF610_ADC_CALF 0x2
#define VF610_ADC_TIMEOUT msecs_to_jiffies(100)
+#define DEFAULT_SAMPLE_TIME 1000
+
enum clk_sel {
VF610_ADCIOC_BUSCLK_SET,
VF610_ADCIOC_ALTCLK_SET,
@@ -118,15 +123,34 @@ enum average_sel {
VF610_ADC_SAMPLE_32,
};
+enum conversion_mode_sel {
+ VF610_ADC_CONV_NORMAL,
+ VF610_ADC_CONV_HIGH_SPEED,
+ VF610_ADC_CONV_LOW_POWER,
+};
+
+enum lst_adder_sel {
+ VF610_ADCK_CYCLES_3,
+ VF610_ADCK_CYCLES_5,
+ VF610_ADCK_CYCLES_7,
+ VF610_ADCK_CYCLES_9,
+ VF610_ADCK_CYCLES_13,
+ VF610_ADCK_CYCLES_17,
+ VF610_ADCK_CYCLES_21,
+ VF610_ADCK_CYCLES_25,
+};
+
struct vf610_adc_feature {
enum clk_sel clk_sel;
enum vol_ref vol_ref;
+ enum conversion_mode_sel conv_mode;
int clk_div;
int sample_rate;
int res_mode;
+ u32 lst_adder_index;
+ u32 default_sample_time;
- bool lpm;
bool calibration;
bool ovwren;
};
@@ -139,6 +163,8 @@ struct vf610_adc {
u32 vref_uv;
u32 value;
struct regulator *vref;
+
+ u32 max_adck_rate[3];
struct vf610_adc_feature adc_feature;
u32 sample_freq_avail[5];
@@ -147,47 +173,40 @@ struct vf610_adc {
};
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
-
-#define VF610_ADC_CHAN(_idx, _chan_type) { \
- .type = (_chan_type), \
- .indexed = 1, \
- .channel = (_idx), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_SAMP_FREQ), \
-}
-
-#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
- .type = (_chan_type), \
- .channel = (_idx), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
-}
-
-static const struct iio_chan_spec vf610_adc_iio_channels[] = {
- VF610_ADC_CHAN(0, IIO_VOLTAGE),
- VF610_ADC_CHAN(1, IIO_VOLTAGE),
- VF610_ADC_CHAN(2, IIO_VOLTAGE),
- VF610_ADC_CHAN(3, IIO_VOLTAGE),
- VF610_ADC_CHAN(4, IIO_VOLTAGE),
- VF610_ADC_CHAN(5, IIO_VOLTAGE),
- VF610_ADC_CHAN(6, IIO_VOLTAGE),
- VF610_ADC_CHAN(7, IIO_VOLTAGE),
- VF610_ADC_CHAN(8, IIO_VOLTAGE),
- VF610_ADC_CHAN(9, IIO_VOLTAGE),
- VF610_ADC_CHAN(10, IIO_VOLTAGE),
- VF610_ADC_CHAN(11, IIO_VOLTAGE),
- VF610_ADC_CHAN(12, IIO_VOLTAGE),
- VF610_ADC_CHAN(13, IIO_VOLTAGE),
- VF610_ADC_CHAN(14, IIO_VOLTAGE),
- VF610_ADC_CHAN(15, IIO_VOLTAGE),
- VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
- /* sentinel */
-};
+static const u32 vf610_lst_adder[] = { 3, 5, 7, 9, 13, 17, 21, 25 };
static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
- int i;
+ u32 adck_period, lst_addr_min;
+ int divisor, i;
+
+ adck_rate = info->max_adck_rate[adc_feature->conv_mode];
+
+ if (adck_rate) {
+ /* calculate clk divider which is within specification */
+ divisor = ipg_rate / adck_rate;
+ adc_feature->clk_div = 1 << fls(divisor + 1);
+ } else {
+ /* fall-back value using a safe divisor */
+ adc_feature->clk_div = 8;
+ }
+
+ adck_rate = ipg_rate / info->adc_feature.clk_div;
+
+ /*
+ * Determine the long sample time adder value to be used based
+ * on the default minimum sample time provided.
+ */
+ adck_period = NSEC_PER_SEC / adck_rate;
+ lst_addr_min = adc_feature->default_sample_time / adck_period;
+ for (i = 0; i < ARRAY_SIZE(vf610_lst_adder); i++) {
+ if (vf610_lst_adder[i] > lst_addr_min) {
+ adc_feature->lst_adder_index = i;
+ break;
+ }
+ }
/*
* Calculate ADC sample frequencies
@@ -198,12 +217,12 @@ static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
* SFCAdder: fixed to 6 ADCK cycles
* AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
* BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
- * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
+ * LSTAdder(Long Sample Time): 3, 5, 7, 9, 13, 17, 21, 25 ADCK cycles
*/
- adck_rate = ipg_rate / info->adc_feature.clk_div;
for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
info->sample_freq_avail[i] =
- adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3));
+ adck_rate / (6 + vf610_hw_avgs[i] *
+ (25 + vf610_lst_adder[adc_feature->lst_adder_index]));
}
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
@@ -219,10 +238,8 @@ static inline void vf610_adc_cfg_init(struct vf610_adc *info)
adc_feature->res_mode = 12;
adc_feature->sample_rate = 1;
- adc_feature->lpm = true;
- /* Use a save ADCK which is below 20MHz on all devices */
- adc_feature->clk_div = 8;
+ adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER;
vf610_adc_calculate_rates(info);
}
@@ -304,10 +321,12 @@ static void vf610_adc_cfg_set(struct vf610_adc *info)
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
cfg_data &= ~VF610_ADC_ADLPC_EN;
- if (adc_feature->lpm)
+ if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER)
cfg_data |= VF610_ADC_ADLPC_EN;
cfg_data &= ~VF610_ADC_ADHSC_EN;
+ if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED)
+ cfg_data |= VF610_ADC_ADHSC_EN;
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
}
@@ -363,8 +382,40 @@ static void vf610_adc_sample_set(struct vf610_adc *info)
break;
}
- /* Use the short sample mode */
- cfg_data &= ~(VF610_ADC_ADLSMP_LONG | VF610_ADC_ADSTS_MASK);
+ /*
+ * Set ADLSMP and ADSTS based on the Long Sample Time Adder value
+ * determined.
+ */
+ switch (adc_feature->lst_adder_index) {
+ case VF610_ADCK_CYCLES_3:
+ break;
+ case VF610_ADCK_CYCLES_5:
+ cfg_data |= VF610_ADC_ADSTS_SHORT;
+ break;
+ case VF610_ADCK_CYCLES_7:
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ case VF610_ADCK_CYCLES_9:
+ cfg_data |= VF610_ADC_ADSTS_LONG;
+ break;
+ case VF610_ADCK_CYCLES_13:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ break;
+ case VF610_ADCK_CYCLES_17:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_SHORT;
+ break;
+ case VF610_ADCK_CYCLES_21:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ case VF610_ADCK_CYCLES_25:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ default:
+ dev_err(info->dev, "error in sample time select\n");
+ }
/* update hardware average selection */
cfg_data &= ~VF610_ADC_AVGS_MASK;
@@ -409,6 +460,81 @@ static void vf610_adc_hw_init(struct vf610_adc *info)
vf610_adc_cfg_set(info);
}
+static int vf610_set_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ info->adc_feature.conv_mode = mode;
+ vf610_adc_calculate_rates(info);
+ vf610_adc_hw_init(info);
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+
+static int vf610_get_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+
+ return info->adc_feature.conv_mode;
+}
+
+static const char * const vf610_conv_modes[] = { "normal", "high-speed",
+ "low-power" };
+
+static const struct iio_enum vf610_conversion_mode = {
+ .items = vf610_conv_modes,
+ .num_items = ARRAY_SIZE(vf610_conv_modes),
+ .get = vf610_get_conversion_mode,
+ .set = vf610_set_conversion_mode,
+};
+
+static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
+ IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode),
+ {},
+};
+
+#define VF610_ADC_CHAN(_idx, _chan_type) { \
+ .type = (_chan_type), \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .ext_info = vf610_ext_info, \
+}
+
+#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
+ .type = (_chan_type), \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+}
+
+static const struct iio_chan_spec vf610_adc_iio_channels[] = {
+ VF610_ADC_CHAN(0, IIO_VOLTAGE),
+ VF610_ADC_CHAN(1, IIO_VOLTAGE),
+ VF610_ADC_CHAN(2, IIO_VOLTAGE),
+ VF610_ADC_CHAN(3, IIO_VOLTAGE),
+ VF610_ADC_CHAN(4, IIO_VOLTAGE),
+ VF610_ADC_CHAN(5, IIO_VOLTAGE),
+ VF610_ADC_CHAN(6, IIO_VOLTAGE),
+ VF610_ADC_CHAN(7, IIO_VOLTAGE),
+ VF610_ADC_CHAN(8, IIO_VOLTAGE),
+ VF610_ADC_CHAN(9, IIO_VOLTAGE),
+ VF610_ADC_CHAN(10, IIO_VOLTAGE),
+ VF610_ADC_CHAN(11, IIO_VOLTAGE),
+ VF610_ADC_CHAN(12, IIO_VOLTAGE),
+ VF610_ADC_CHAN(13, IIO_VOLTAGE),
+ VF610_ADC_CHAN(14, IIO_VOLTAGE),
+ VF610_ADC_CHAN(15, IIO_VOLTAGE),
+ VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
+ /* sentinel */
+};
+
static int vf610_adc_read_data(struct vf610_adc *info)
{
int result;
@@ -651,6 +777,14 @@ static int vf610_adc_probe(struct platform_device *pdev)
info->vref_uv = regulator_get_voltage(info->vref);
+ of_property_read_u32_array(pdev->dev.of_node, "fsl,adck-max-frequency",
+ info->max_adck_rate, 3);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "min-sample-time",
+ &info->adc_feature.default_sample_time);
+ if (ret)
+ info->adc_feature.default_sample_time = DEFAULT_SAMPLE_TIME;
+
platform_set_drvdata(pdev, indio_dev);
init_completion(&info->completion);