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.c146
1 files changed, 104 insertions, 42 deletions
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index 56292ae4538d..480f335a0f9f 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -118,15 +118,21 @@ 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,
+};
+
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;
- bool lpm;
bool calibration;
bool ovwren;
};
@@ -139,6 +145,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];
@@ -148,46 +156,22 @@ 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 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;
+ 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;
+ }
/*
* Calculate ADC sample frequencies
@@ -219,10 +203,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 +286,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);
}
@@ -409,6 +393,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 +710,9 @@ 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);
+
platform_set_drvdata(pdev, indio_dev);
init_completion(&info->completion);