diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-24.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-24.dts index 2895bbae17a3b..0092b335a1406 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-24.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-24.dts @@ -39,6 +39,11 @@ regulator-always-on; }; + trigger_pwm: adc-pwm-trigger { + compatible = "pwm-trigger"; + #trigger-source-cells = <0>; + pwms = <&adc_trigger 0 0 0>; + }; clocks { cnv_ext_clk: ext-clk { #clock-cells = <0x0>; @@ -50,6 +55,13 @@ }; &fpga_axi { + adc_trigger: pwm@44b00000 { + compatible = "adi,axi-pwmgen-2.00.a"; + reg = <0x44b00000 0x1000>; + #pwm-cells = <2>; + clocks = <&cnv_ext_clk>; + }; + rx_dma: rx-dmac@44a30000 { compatible = "adi,axi-dmac-1.00.a"; reg = <0x44a30000 0x1000>; @@ -67,23 +79,17 @@ clock-output-names = "spi_clk"; }; - axi_pwm_gen: axi-pwm-gen@ { - compatible = "adi,axi-pwmgen-2.00.a"; - reg = <0x44b00000 0x1000>; - label = "ad463x_cnv"; - #pwm-cells = <2>; - clocks = <&cnv_ext_clk>; - - }; - axi_spi_engine: spi@44a00000 { - compatible = "adi-ex,axi-spi-engine-1.00.a"; + compatible = "adi,axi-spi-engine-1.00.a"; reg = <0x44a00000 0x1FF>; interrupt-parent = <&intc>; interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clkc 15>, <&spi_clk>; clock-names = "s_axi_aclk", "spi_clk"; - num-cs = <1>; + + dmas = <&rx_dma 0>; + dma-names = "offload0-rx"; + trigger-sources = <&trigger_pwm>; #address-cells = <0x1>; #size-cells = <0x0>; @@ -91,22 +97,17 @@ ad4630_24: ad4630-24@0 { compatible = "adi,ad4630-24"; reg = <0>; + spi-max-frequency = <80000000>; vdd-supply = <&vref>; vdd_1_8-supply = <&vdd_1_8>; vio-supply = <&vio>; vref-supply = <&vref>; - spi-max-frequency = <80000000>; + pwm-names = "cnv"; + pwms = <&adc_trigger 1 100000>; reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; adi,lane-mode = <1>; adi,clock-mode = <0>; adi,out-data-mode = <0>; - adi,spi-trigger; - clocks = <&cnv_ext_clk>; - clock-names = "trigger_clock"; - dmas = <&rx_dma 0>; - dma-names = "rx"; - pwm-names = "spi_trigger", "cnv"; - pwms = <&axi_pwm_gen 0 0>, <&axi_pwm_gen 1 0>; }; }; }; diff --git a/drivers/iio/adc/ad4630.c b/drivers/iio/adc/ad4630.c index ebe11a53168e0..b983e27afd104 100644 --- a/drivers/iio/adc/ad4630.c +++ b/drivers/iio/adc/ad4630.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -193,9 +193,7 @@ struct ad4630_state { const struct ad4630_chip_info *chip; struct regulator_bulk_data regulators[3]; struct pwm_device *conv_trigger; - struct pwm_device *fetch_trigger; struct pwm_waveform conv_wf; - struct pwm_waveform fetch_wf; struct gpio_descs *pga_gpios; struct spi_device *spi; struct regmap *regmap; @@ -210,8 +208,9 @@ struct ad4630_state { /* offload sampling spi message */ struct spi_transfer offload_xfer; struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; - bool use_spi_trigger; u8 bits_per_word; u8 pattern_bits_per_word; @@ -373,7 +372,7 @@ static int ad4630_read_avail(struct iio_dev *indio_dev, static int __ad4630_set_sampling_freq(struct ad4630_state *st, unsigned int freq) { - struct pwm_waveform conv_wf = { }, fetch_wf = { .duty_length_ns = 10 }; + struct pwm_waveform conv_wf = { }; int ret; u64 target = AD4630_TCNV_HIGH_NS; @@ -394,42 +393,7 @@ static int __ad4630_set_sampling_freq(struct ad4630_state *st, unsigned int freq target += 10; } while (conv_wf.duty_length_ns < 10); - if (!st->fetch_trigger) - return 0; - - fetch_wf.period_length_ns = conv_wf.period_length_ns; - - if (st->out_data == AD4630_30_AVERAGED_DIFF) { - u32 avg; - - ret = regmap_read(st->regmap, AD4630_REG_AVG, &avg); - if (ret) - return ret; - - fetch_wf.period_length_ns <<= FIELD_GET(AD4630_AVG_AVG_VAL, avg); - } - - /* - * The hardware does the capture on zone 2 (when spi trigger PWM - * is used). This means that the spi trigger signal should happen at - * tsync + tquiet_con_delay being tsync the conversion signal period - * and tquiet_con_delay 9.8ns. Hence set the PWM phase accordingly. - * - * The PWM waveform API only supports nanosecond resolution right now, - * so round this setting to the closest available value. - */ - target = AD4630_TQUIET_CNV_DELAY_NS; - - do { - fetch_wf.duty_offset_ns = target; - ret = pwm_round_waveform_might_sleep(st->fetch_trigger, &fetch_wf); - if (ret) - return ret; - target += 10; - } while (fetch_wf.duty_offset_ns < AD4630_TQUIET_CNV_DELAY_NS); - st->conv_wf = conv_wf; - st->fetch_wf = fetch_wf; return 0; } @@ -612,25 +576,6 @@ static int ad4630_write_raw(struct iio_dev *indio_dev, } } -static int ad4630_update_sample_fetch_trigger(struct ad4630_state *st, u32 avg) -{ - struct pwm_waveform fetch_wf = st->fetch_wf; - int ret; - - if (!st->fetch_trigger) - return 0; - - fetch_wf.period_length_ns = st->conv_wf.period_length_ns << avg; - - ret = pwm_round_waveform_might_sleep(st->fetch_trigger, &fetch_wf); - if (ret) - return ret; - - st->fetch_wf = fetch_wf; - - return 0; -} - static int ad4630_set_avg_frame_len(struct iio_dev *dev, const struct iio_chan_spec *chan, unsigned int avg_len) @@ -643,11 +588,7 @@ static int ad4630_set_avg_frame_len(struct iio_dev *dev, return ret; ret = regmap_write(st->regmap, AD4630_REG_AVG, avg_len + 1); - if (ret) - goto out_error; - ret = ad4630_update_sample_fetch_trigger(st, avg_len + 1); -out_error: iio_device_release_direct_mode(dev); return ret; @@ -684,26 +625,8 @@ static int ad4630_spi_transfer_update(struct ad4630_state *st) if (FIELD_GET(AD4630_OUT_DATA_MODE_MSK, mode) == AD4630_32_PATTERN) { bits_per_w = st->pattern_bits_per_word; - /* - * If the previous mode is averaging, we need to update the - * fetch PWM signal as there's no averaging in the test pattern - * mode and the user might have already configured some - * averaging. - */ - if (st->out_data == AD4630_30_AVERAGED_DIFF) - ad4630_update_sample_fetch_trigger(st, 0); } else { bits_per_w = st->bits_per_word; - /* Restore the fetch PWM signal */ - if (st->out_data == AD4630_30_AVERAGED_DIFF) { - u32 avg; - - ret = regmap_read(st->regmap, AD4630_REG_AVG, &avg); - if (ret) - return ret; - - ad4630_update_sample_fetch_trigger(st, avg); - } } st->offload_xfer.bits_per_word = bits_per_w; @@ -716,6 +639,11 @@ static int ad4630_buffer_preenable(struct iio_dev *indio_dev) struct ad4630_state *st = iio_priv(indio_dev); int ret, read_ret; u32 dummy; + u64 offload_period_ns; + u64 offload_offset_ns; + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + }; ret = pm_runtime_resume_and_get(&st->spi->dev); if (ret < 0) @@ -730,30 +658,57 @@ static int ad4630_buffer_preenable(struct iio_dev *indio_dev) if (ret) goto out_error; + st->offload_msg.offload = st->offload; ret = spi_optimize_message(st->spi, &st->offload_msg); if (ret < 0) goto out_reset_mode; spi_bus_lock(st->spi->master); - spi_engine_ex_offload_load_msg(st->spi, &st->offload_msg); - spi_engine_ex_offload_enable(st->spi, true); ret = pwm_set_waveform_might_sleep(st->conv_trigger, &st->conv_wf, false); if (ret) - goto out_offload_disable; + goto out_unlock; - if (!st->fetch_trigger) + if (!st->offload_trigger) return 0; - ret = pwm_set_waveform_might_sleep(st->fetch_trigger, &st->fetch_wf, false); - if (ret) - goto out_disable_pwm; + offload_period_ns = st->conv_wf.period_length_ns; + if (st->out_data == AD4630_30_AVERAGED_DIFF) { + u32 avg; + + ret = regmap_read(st->regmap, AD4630_REG_AVG, &avg); + if (ret) + goto out_pwm_disable; + + offload_period_ns <<= FIELD_GET(AD4630_AVG_AVG_VAL, avg); + } + + config.periodic.frequency_hz = DIV_ROUND_UP_ULL(NSEC_PER_SEC, + offload_period_ns); + + /* + * The hardware does the capture on zone 2 (when spi trigger PWM + * is used). This means that the spi trigger signal should happen at + * tsync + tquiet_con_delay being tsync the conversion signal period + * and tquiet_con_delay 9.8ns. Hence set the PWM phase accordingly. + * + * The PWM waveform API only supports nanosecond resolution right now, + * so round this setting to the closest available value. + */ + offload_offset_ns = AD4630_TQUIET_CNV_DELAY_NS; + do { + config.periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &config); + if (ret) + goto out_pwm_disable; + offload_offset_ns += 10; + + } while (config.periodic.offset_ns < AD4630_TQUIET_CNV_DELAY_NS); return 0; -out_disable_pwm: +out_pwm_disable: pwm_disable(st->conv_trigger); -out_offload_disable: - spi_engine_ex_offload_enable(st->spi, false); +out_unlock: spi_bus_unlock(st->spi->master); spi_unoptimize_message(&st->offload_msg); out_reset_mode: @@ -773,10 +728,9 @@ static int ad4630_buffer_postdisable(struct iio_dev *indio_dev) u32 dummy; int ret; - pwm_disable(st->fetch_trigger); pwm_disable(st->conv_trigger); - spi_engine_ex_offload_enable(st->spi, false); + spi_offload_trigger_disable(st->offload, st->offload_trigger); spi_bus_unlock(st->spi->master); spi_unoptimize_message(&st->offload_msg); @@ -1108,11 +1062,6 @@ static const struct ad4630_chip_info ad4630_chip_info[] = { } }; -static void ad4630_clk_disable(void *data) -{ - clk_disable_unprepare(data); -} - static void ad4630_regulator_disable(void *data) { regulator_disable(data); @@ -1233,24 +1182,6 @@ static int ad4630_pwm_get(struct ad4630_state *st) */ pwm_disable(st->conv_trigger); - /* - * the trigger is optional... We can have the device BUSY pin - * acting as trigger. - */ - if (!st->use_spi_trigger) - goto out_sampling_freq; - - st->fetch_trigger = devm_pwm_get(dev, "spi_trigger"); - if (IS_ERR(st->fetch_trigger)) - return dev_err_probe(dev, PTR_ERR(st->fetch_trigger), - "Failed to get spi engine pwm\n"); - /* - * Preemptively disable the PWM, since we only want to enable it with - * the buffer - */ - pwm_disable(st->fetch_trigger); - -out_sampling_freq: return __ad4630_set_sampling_freq(st, st->max_rate); } @@ -1260,12 +1191,6 @@ static void ad4630_prepare_spi_sampling_msg(struct ad4630_state *st, { const struct ad4630_out_mode *out_mode = &st->chip->modes[st->out_data]; int data_width = out_mode->data_width; - /* - * Receive buffer needs to be non-zero for the SPI engine controller - * to mark the transfer as a read. - */ - st->offload_xfer.speed_hz = AD4630_SPI_SAMPLING_SPEED; - st->offload_xfer.rx_buf = (void *)-1; /* * In host mode, for a 16-bit data-word, the device adds an additional @@ -1292,7 +1217,7 @@ static void ad4630_prepare_spi_sampling_msg(struct ad4630_state *st, st->offload_xfer.bits_per_word = st->bits_per_word; st->offload_xfer.len = roundup_pow_of_two(BITS_TO_BYTES(st->bits_per_word)); - + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); } @@ -1347,8 +1272,6 @@ static int ad4630_config(struct ad4630_state *st) reg_modes |= FIELD_PREP(AD4630_OUT_DATA_MODE_MSK, st->out_data); } - st->use_spi_trigger = device_property_read_bool(dev, "adi,spi-trigger"); - if (st->vio < 1400000) { /* * for VIO levels below 1.4 V, the IO2X bit in the output driver @@ -1500,11 +1423,16 @@ static void ad4630_debugs_init(struct iio_dev *indio_dev) indio_dev, &ad4630_test_pattern_en_fops); } +static const struct spi_offload_config ad4630_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + static int ad4630_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct iio_dev *indio_dev; - struct clk *trigger_clock; + struct dma_chan *rx_dma; struct ad4630_state *st; int ret; @@ -1521,26 +1449,23 @@ static int ad4630_probe(struct spi_device *spi) return dev_err_probe(dev, -ENODEV, "Could not find chip info data\n"); + st->offload = devm_spi_offload_get(&spi->dev, spi, &ad4630_offload_config); + if (PTR_ERR_OR_ZERO(st->offload)) + return dev_err_probe(&spi->dev, ret, "failed to get offload\n"); + + /* trigger is optional as busy signal can be used as trigger*/ + st->offload_trigger = devm_spi_offload_trigger_get(dev, + st->offload, SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + st->regmap = devm_regmap_init(&spi->dev, &ad4630_regmap_bus, st, &ad4630_regmap_config); if (IS_ERR(st->regmap)) dev_err_probe(&spi->dev, PTR_ERR(st->regmap), "Failed to initialize regmap\n"); - trigger_clock = devm_clk_get(dev, "trigger_clock"); - if (IS_ERR(trigger_clock)) - return dev_err_probe(dev, PTR_ERR(trigger_clock), - "Failed to get trigger clk\n"); - - ret = clk_prepare_enable(trigger_clock); - if (ret) - return dev_err_probe(dev, ret, - "failed to enable trigger clk\n"); - - ret = devm_add_action_or_reset(dev, ad4630_clk_disable, trigger_clock); - if (ret) - return ret; - ret = ad4630_regulators_get(st); if (ret) return ret; @@ -1587,7 +1512,13 @@ static int ad4630_probe(struct spi_device *spi) indio_dev->available_scan_masks = st->chip->available_masks; indio_dev->setup_ops = &ad4630_buffer_setup_ops; - ret = devm_iio_dmaengine_buffer_setup(dev, indio_dev, "rx"); + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); if (ret) return dev_err_probe(dev, ret, "Failed to get DMA buffer\n"); diff --git a/drivers/spi/spi-offload-trigger-pwm.c b/drivers/spi/spi-offload-trigger-pwm.c index 805ed41560df0..8413aeb3689d1 100644 --- a/drivers/spi/spi-offload-trigger-pwm.c +++ b/drivers/spi/spi-offload-trigger-pwm.c @@ -51,13 +51,13 @@ static int spi_offload_trigger_pwm_validate(struct spi_offload_trigger *trigger, wf.period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, periodic->frequency_hz); /* REVISIT: 50% duty-cycle for now - may add config parameter later */ wf.duty_length_ns = wf.period_length_ns / 2; - + wf.duty_offset_ns = periodic->offset_ns; ret = pwm_round_waveform_might_sleep(st->pwm, &wf); if (ret < 0) return ret; periodic->frequency_hz = DIV_ROUND_UP_ULL(NSEC_PER_SEC, wf.period_length_ns); - + periodic->offset_ns = wf.duty_offset_ns; return 0; } @@ -77,6 +77,7 @@ static int spi_offload_trigger_pwm_enable(struct spi_offload_trigger *trigger, wf.period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, periodic->frequency_hz); /* REVISIT: 50% duty-cycle for now - may add config parameter later */ wf.duty_length_ns = wf.period_length_ns / 2; + wf.duty_offset_ns = periodic->offset_ns; return pwm_set_waveform_might_sleep(st->pwm, &wf, false); } diff --git a/include/linux/spi/offload/types.h b/include/linux/spi/offload/types.h index 6f78923478713..0170fd1f42e5c 100644 --- a/include/linux/spi/offload/types.h +++ b/include/linux/spi/offload/types.h @@ -59,6 +59,7 @@ enum spi_offload_trigger_type { struct spi_offload_trigger_periodic { u64 frequency_hz; + u64 offset_ns; }; struct spi_offload_trigger_config {