Skip to content

Commit

Permalink
pinmux hack
Browse files Browse the repository at this point in the history
  • Loading branch information
dlech committed Nov 22, 2023
1 parent 02c7731 commit 91b94a4
Show file tree
Hide file tree
Showing 6 changed files with 622 additions and 231 deletions.
40 changes: 39 additions & 1 deletion Documentation/devicetree/bindings/spi/adi,axi-spi-engine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,33 @@ properties:
$ref: '#/$defs/offload'
additionalProperties: true

patternProperties:
'-pinmux$':
type: object
$ref: /schemas/pinctrl/pinmux-node.yaml
description:
TODO

properties:
pins:
items:
enum:
- CS0
- CS1
- CS2
- CS3
- CS4
- CS5
- CS6
- CS7

function:
enum:
- spi_cs
- gpio_out

additionalProperties: false

required:
- compatible
- reg
Expand All @@ -86,16 +113,24 @@ unevaluatedProperties: false

examples:
- |
spi@44a00000 {
spi_engine: spi@44a00000 {
compatible = "adi,axi-spi-engine-1.00.a";
reg = <0x44a00000 0x1000>;
interrupts = <0 56 4>;
clocks = <&clkc 15>, <&clkc 15>;
clock-names = "s_axi_aclk", "spi_clk";
gpio-controller;
#gpio-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
adc0_pins: adc0-pinmux {
pins = "CS0", "CS1";
functions = "spi_cs", "gpio_out";
};
offloads {
#address-cells = <1>;
#size-cells = <0>;
Expand All @@ -109,8 +144,11 @@ examples:
};
adc@0 {
pinctlr-0 = <&adc0_pins>;
pinctrl-names = "default";
compatible = "adi,example-adc";
reg = <0>;
adi,offloads = <&offload_0>;
cnv-gpios = <&spi_engine 1>;
};
};
11 changes: 11 additions & 0 deletions arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7985.dts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,17 @@
clocks = <&clkc 15>, <&spi_clk>;
clock-names = "s_axi_aclk", "spi_clk";

gpio-controller;
#gpio-cells = <2>;

#address-cells = <0x1>;
#size-cells = <0x0>;

adc_pins: adc-pinmux {
pins = "CS0";
function = "gpio_out";
};

offloads {
#address-cells = <0x1>;
#size-cells = <0x0>;
Expand All @@ -96,13 +104,16 @@
};

ad7985: adc@0 {
pinctrl-0 = <&adc_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
compatible = "adi,ad7985";
reg = <0>;
spi-cpha;
spi-max-frequency = <111111111>; /* 9 ns period */
adi,spi-mode = "3-wire";
cnv-gpios = <&axi_spi_engine_0 0 GPIO_ACTIVE_HIGH>;
turbo-gpios = <&gpio0 87 GPIO_ACTIVE_HIGH>;
ref-supply = <&vref>;
adi,offloads = <&offload_0>;
Expand Down
194 changes: 105 additions & 89 deletions drivers/iio/adc/ad7944.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ static const struct ad7944_chip_info ad7986_chip_info = {
struct ad7944_adc {
const struct ad7944_chip_info *info;
struct spi_device *spi;
/** CNV pin GPIO.*/
struct gpio_desc *cnv;
/** Optional TURBO mode GPIO. */
struct gpio_desc *turbo;
/** Indicates TURBO is hard-wired to be enabled. */
Expand All @@ -75,6 +77,8 @@ struct ad7944_adc {
struct iio_trigger *offload_trigger;
/** Optional SPI Engine offload handle. */
struct spi_offload *spi_offload;
/** CNV pin ID for offload commands. */
int spi_offload_cnv_gpio_id;

/*
* DMA (thus cache coherency maintenance) requires the
Expand Down Expand Up @@ -162,6 +166,12 @@ static int ad7944_read_channel(struct ad7944_adc *adc, int *val)
},
};

// TODO: proper syncronization with offload
if (!mutex_trylock(&adc->spi->controller->bus_lock_mutex))
return -EBUSY;

mutex_unlock(&adc->spi->controller->bus_lock_mutex);

ret = spi_sync_transfer(adc->spi, xfers, ARRAY_SIZE(xfers));
// ret = spi_sync_transfer(adc->spi, &xfer, 1);
if (ret)
Expand Down Expand Up @@ -215,12 +225,22 @@ static int ad7944_read_raw(struct iio_dev *indio_dev,
static int ad7944_buffer_preenable(struct iio_dev *indio_dev)
{
struct ad7944_adc *adc = iio_priv(indio_dev);
int ret;
bool requires_32_bits = adc->info->resolution > 16;
struct spi_transfer xfer = {
/*
* offload doesn't actually write here, we just need to populate
* this field to indicate that this as a read transfer
*/
.rx_buf = &adc->raw_sample,
.len = adc->info->resolution > 16 ? 4 : 2,
.bits_per_word = adc->info->resolution,
};
struct spi_message msg;
bool read_during_conversion = false;
bool use_turbo = false;
u32 trig_hz;

spi_message_init_with_transfers(&msg, &xfer, 1);

/*
* It only makes sense to use turbo mode and/or read during conversion
* if there is SPI offload support and the trigger rate is high enough.
Expand Down Expand Up @@ -258,106 +278,90 @@ static int ad7944_buffer_preenable(struct iio_dev *indio_dev)
* fast enough read everything during the conversion time,
* otherwise, we get bad data.
*/
struct spi_transfer xfers[] = {
{
/* change CNV to high to trigger conversion */
.cs_off = 1,
.bits_per_word = adc->info->resolution,
.delay = {
/* t[CNVH] */
.value = 10,
.unit = SPI_DELAY_UNIT_NSECS,
},
},
struct spi_offload_cmd cmds[] = {
{
/* read sample data from previous conversion */
.rx_buf = &adc->raw_sample,
.len = requires_32_bits ? 4 : 2,
.bits_per_word = adc->info->resolution,
/*
* CNV has to be high at end of conversion to
* avoid triggering the busy signal on the SDO
* line.
*/
.cs_change = 1,
.cs_change_delay = {
/* t[CONV] - t[CNVH] - t[EN] - t[DATA] */
.value = (use_turbo ? 320 : 420) - 10 -
5 - (use_turbo ? 190 : 240),
.unit = SPI_DELAY_UNIT_NSECS,
},
},
{
.bits_per_word = adc->info->resolution,
/*
* since this is the last xfer, this changes CNV
* to low and keeps it there until the next xfer
*/
.cs_change = 1,
},
.type = SPI_OFFLOAD_CMD_GPIO_OUT,
.gpio_out.id = adc->spi_offload_cnv_gpio_id,
.gpio_out.value = 1,
}, {
.type = SPI_OFFLOAD_CMD_DELAY,
.delay_ns = 10, /* t[CNVH]*/
}, {
.type = SPI_OFFLOAD_CMD_GPIO_OUT,
.gpio_out.id = adc->spi_offload_cnv_gpio_id,
.gpio_out.value = 0,
// TODO: this can be removed when we have proper
// HDL support for GPIOs
}, {
.type = SPI_OFFLOAD_CMD_SPI_MSG,
.spi_msg = &msg,
}, {
.type = SPI_OFFLOAD_CMD_GPIO_OUT,
.gpio_out.id = adc->spi_offload_cnv_gpio_id,
.gpio_out.value = 1,
// TODO: this can be removed when we have proper
// HDL support for GPIOs
}, {
.type = SPI_OFFLOAD_CMD_DELAY,
/* t[CONV] - t[CNVH] - t[EN] - t[DATA] */
.delay_ns = (use_turbo ? 320 : 420) - 10 - 5
- (use_turbo ? 190 : 240),
}, {
.type = SPI_OFFLOAD_CMD_GPIO_OUT,
.gpio_out.id = adc->spi_offload_cnv_gpio_id,
.gpio_out.value = 0,
}
};

ret = spi_offload_prepare(adc->spi_offload, adc->spi, xfers,
ARRAY_SIZE(xfers));
if (ret)
return ret;
return spi_offload_prepare(adc->spi_offload, adc->spi, cmds,
ARRAY_SIZE(cmds));

// TODO: validate that xfer->effective_speed_hz fits required timing.
// i.e. total data read time < t[CONV] and total time < sample period
} else {
/*
* This is using read during acquire mode. The CNV signal must be kept
* high for > t[CONV] to avoid triggering the busy signal on the SDO
* line. It is not possible to achieve the maximum transfer rate when
* reading like this.
*/

// TODO: share this xfer setup with ad7944_read_channel()
struct spi_transfer xfers[] = {
{
/* change CNV to high to trigger conversion */
.cs_off = 1,
.bits_per_word = adc->info->resolution,
.delay = {
/* t[CONV] + t[EN] */
.value = (use_turbo ? 320 : 420) + 20,
.unit = SPI_DELAY_UNIT_NSECS,
},
},
{
/* CNV has to stay high during read */
.cs_off = 1,
/* read sample data */
.rx_buf = &adc->raw_sample,
.len = requires_32_bits ? 4 : 2,
.bits_per_word = adc->info->resolution,
},
struct spi_offload_cmd cmds[] = {
{
.bits_per_word = adc->info->resolution,
/*
* since this is the last xfer, this changes CNV
* to low and keeps it there until the next xfer
*/
.cs_change = 1,
.cs_change_delay = {
/* I/O quite time (t[QUIET]) */
.value = 20,
.unit = SPI_DELAY_UNIT_NSECS,
},
},
.type = SPI_OFFLOAD_CMD_GPIO_OUT,
.gpio_out.id = adc->spi_offload_cnv_gpio_id,
.gpio_out.value = 1,
}, {
.type = SPI_OFFLOAD_CMD_DELAY,
.delay_ns = (use_turbo ? 320 : 420), /* t[CONV]*/
}, {
.type = SPI_OFFLOAD_CMD_GPIO_OUT,
.gpio_out.id = adc->spi_offload_cnv_gpio_id,
.gpio_out.value = 0,
}, {
.type = SPI_OFFLOAD_CMD_SPI_MSG,
.spi_msg = &msg,
}
// TODO: the last two instructions should be swapped
// when we have proper HDL support for GPIOs
};

ret = spi_offload_prepare(adc->spi_offload, adc->spi, xfers,
ARRAY_SIZE(xfers));
if (ret)
return ret;
return spi_offload_prepare(adc->spi_offload, adc->spi, cmds,
ARRAY_SIZE(cmds));
}
}

// TODO: validate that xfer->effective_speed_hz fits required timing.
// i.e. total data read time < t[CONV] and total time < sample period
static int ad7944_buffer_postenable(struct iio_dev *indio_dev)
{
struct ad7944_adc *adc = iio_priv(indio_dev);

ret = spi_offload_enable(adc->spi_offload);
if (ret) {
spi_offload_unprepare(adc->spi_offload);
return ret;
}
return spi_offload_enable(adc->spi_offload);;
}

static int ad7944_buffer_predisable(struct iio_dev *indio_dev)
{
struct ad7944_adc *adc = iio_priv(indio_dev);

spi_offload_disable(adc->spi_offload);

return 0;
}
Expand All @@ -366,16 +370,16 @@ static int ad7944_buffer_postdisable(struct iio_dev *indio_dev)
{
struct ad7944_adc *adc = iio_priv(indio_dev);

spi_offload_disable(adc->spi_offload);
spi_offload_unprepare(adc->spi_offload);

gpiod_set_value_cansleep(adc->turbo, 1);
gpiod_set_value_cansleep(adc->turbo, 0);

return 0;
}

static const struct iio_buffer_setup_ops ad7944_buffer_ops = {
.preenable = &ad7944_buffer_preenable,
.postenable = &ad7944_buffer_postenable,
.predisable = &ad7944_buffer_predisable,
.postdisable = &ad7944_buffer_postdisable,
};

Expand Down Expand Up @@ -426,6 +430,11 @@ static int ad7944_probe(struct spi_device *spi)
"SPI host does not support %d bits per word\n",
adc->info->resolution);

adc->cnv = devm_gpiod_get(&spi->dev, "cnv", GPIOD_OUT_LOW);
if (IS_ERR(adc->cnv))
return dev_err_probe(&spi->dev, PTR_ERR(adc->cnv),
"failed to get cnv GPIO\n");

adc->turbo = devm_gpiod_get_optional(&spi->dev, "turbo", GPIOD_OUT_LOW);
if (IS_ERR(adc->turbo))
return dev_err_probe(&spi->dev, PTR_ERR(adc->turbo),
Expand Down Expand Up @@ -479,6 +488,13 @@ static int ad7944_probe(struct spi_device *spi)
PTR_ERR(adc->spi_offload),
"failed to get SPI offload\n");

adc->spi_offload_cnv_gpio_id = spi_offload_get_gpio_id(
adc->spi_offload, adc->cnv);
if (adc->spi_offload_cnv_gpio_id < 0)
return dev_err_probe(&spi->dev,
adc->spi_offload_cnv_gpio_id,
"CNV GPIO not usable with offload\n");

indio_dev->setup_ops = &ad7944_buffer_ops;
ret = axi_spi_engine_offload_pwm_trigger_setup(indio_dev,
adc->offload_trigger);
Expand Down
Loading

0 comments on commit 91b94a4

Please sign in to comment.