On 24/03/15 12:47, Stefan Agner wrote: > Support configurable conversion mode through sysfs. So far, the > mode used was low-power, which is enabled by default now. Beside > that, the modes normal and high-speed are selectable as well. > > Use the new device tree property which specifies the maximum ADC > conversion clock frequencies. Depending on the mode used, the > available resulting conversion frequency are calculated > dynamically. > One trivial bit inline, otherwise looks good to me. I wish there was a better way of 'hinting' to drivers what power mode is required, but in the absense of this I guess explicit control is all we can do. > Acked-by: Fugang Duan <B38611@xxxxxxxxxxxxx> > Signed-off-by: Stefan Agner <stefan@xxxxxxxx> > --- > Documentation/ABI/testing/sysfs-bus-iio-vf610 | 7 + > .../devicetree/bindings/iio/adc/vf610-adc.txt | 9 ++ > drivers/iio/adc/vf610_adc.c | 146 +++++++++++++++------ > 3 files changed, 120 insertions(+), 42 deletions(-) > create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-vf610 > > diff --git a/Documentation/ABI/testing/sysfs-bus-iio-vf610 b/Documentation/ABI/testing/sysfs-bus-iio-vf610 > new file mode 100644 > index 0000000..85fd0ecd5 > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-bus-iio-vf610 > @@ -0,0 +1,7 @@ > +What: /sys/bus/iio/devices/iio:deviceX/conversion_mode > +KernelVersion: 4.1 > +Contact: linux-iio@xxxxxxxxxxxxxxx > +Description: > + Specifies the hardware conversion mode used. The three > + available modes are "normal", "high-speed" and "low-power", > + whereas the last is the default mode. Fussy English of the day. where rather than whereas (whereas is only used if you say something like. A is normally the case, whereas here B is the case) I like the default that isn't 'normal' :) > diff --git a/Documentation/devicetree/bindings/iio/adc/vf610-adc.txt b/Documentation/devicetree/bindings/iio/adc/vf610-adc.txt > index 1a4a43d..3eb40e2 100644 > --- a/Documentation/devicetree/bindings/iio/adc/vf610-adc.txt > +++ b/Documentation/devicetree/bindings/iio/adc/vf610-adc.txt > @@ -11,6 +11,13 @@ Required properties: > - clock-names: Must contain "adc", matching entry in the clocks property. > - vref-supply: The regulator supply ADC reference voltage. > > +Recommended properties: > +- fsl,adck-max-frequency: Maximum frequencies according to datasheets operating > + requirements. Three values are required, depending on conversion mode: > + - Frequency in normal mode (ADLPC=0, ADHSC=0) > + - Frequency in high-speed mode (ADLPC=0, ADHSC=1) > + - Frequency in low-power mode (ADLPC=1, ADHSC=0) > + > Example: > adc0: adc@4003b000 { > compatible = "fsl,vf610-adc"; > @@ -18,5 +25,7 @@ adc0: adc@4003b000 { > interrupts = <0 53 0x04>; > clocks = <&clks VF610_CLK_ADC0>; > clock-names = "adc"; > + fsl,adck-max-frequency = <30000000>, <40000000>, > + <20000000>; > vref-supply = <®_vcc_3v3_mcu>; > }; > diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c > index e63b8e7..b5f94ab8 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); > } > @@ -307,10 +289,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); > } > @@ -412,6 +396,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; > @@ -654,6 +713,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); > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html