diff options
| -rw-r--r-- | Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml | 5 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml | 42 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml | 5 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/spi/adi,axi-spi-engine.yaml | 15 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml | 6 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml | 6 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml | 6 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml | 6 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml | 40 | ||||
| -rw-r--r-- | Documentation/spi/index.rst | 1 | ||||
| -rw-r--r-- | Documentation/spi/multiple-data-lanes.rst | 217 | ||||
| -rw-r--r-- | drivers/spi/spi-axi-spi-engine.c | 145 | ||||
| -rw-r--r-- | drivers/spi/spi.c | 144 | ||||
| -rw-r--r-- | include/linux/spi/spi.h | 30 |
14 files changed, 641 insertions, 27 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml b/Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml index 0ce2ea13583d..c35d4f2ab9a4 100644 --- a/Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml +++ b/Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml @@ -34,8 +34,9 @@ properties: spi-cpol: true spi-rx-bus-width: - minimum: 0 - maximum: 1 + items: + minimum: 0 + maximum: 1 dc-gpios: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml index 54e7349317b7..e22d518135f2 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml @@ -37,7 +37,15 @@ properties: maximum: 102040816 spi-rx-bus-width: - enum: [1, 2, 4] + maxItems: 2 + # all lanes must have the same width + oneOf: + - contains: + const: 1 + - contains: + const: 2 + - contains: + const: 4 vdd-5v-supply: true vdd-1v8-supply: true @@ -88,6 +96,18 @@ oneOf: unevaluatedProperties: false +allOf: + - if: + properties: + compatible: + enum: + - adi,ad4030-24 + - adi,ad4032-24 + then: + properties: + spi-rx-bus-width: + maxItems: 1 + examples: - | #include <dt-bindings/gpio/gpio.h> @@ -108,3 +128,23 @@ examples: reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; }; }; + - | + #include <dt-bindings/gpio/gpio.h> + + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4630-24"; + reg = <0>; + spi-max-frequency = <80000000>; + spi-rx-bus-width = <4>, <4>; + vdd-5v-supply = <&supply_5V>; + vdd-1v8-supply = <&supply_1_8V>; + vio-supply = <&supply_1_8V>; + ref-supply = <&supply_5V>; + cnv-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml index cbde7a0505d2..ae8d0b5f328b 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml @@ -38,8 +38,9 @@ properties: spi-cpha: true spi-rx-bus-width: - minimum: 1 - maximum: 4 + items: + minimum: 1 + maximum: 4 avdd-supply: description: Analog power supply. diff --git a/Documentation/devicetree/bindings/spi/adi,axi-spi-engine.yaml b/Documentation/devicetree/bindings/spi/adi,axi-spi-engine.yaml index 4b3828eda6cb..0f2448371f17 100644 --- a/Documentation/devicetree/bindings/spi/adi,axi-spi-engine.yaml +++ b/Documentation/devicetree/bindings/spi/adi,axi-spi-engine.yaml @@ -70,6 +70,21 @@ required: unevaluatedProperties: false +patternProperties: + "^.*@[0-9a-f]+": + type: object + + properties: + spi-rx-bus-width: + maxItems: 8 + items: + enum: [0, 1] + + spi-tx-bus-width: + maxItems: 8 + items: + enum: [0, 1] + examples: - | spi@44a00000 { diff --git a/Documentation/devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml b/Documentation/devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml index e1ab3f523ad6..a34e6471dbe8 100644 --- a/Documentation/devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml +++ b/Documentation/devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml @@ -55,10 +55,12 @@ patternProperties: maximum: 4 spi-rx-bus-width: - const: 1 + items: + - const: 1 spi-tx-bus-width: - const: 1 + items: + - const: 1 required: - compatible diff --git a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml index 1b91d1566c95..a6067030c5ed 100644 --- a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml +++ b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml @@ -81,10 +81,12 @@ patternProperties: maximum: 4 spi-rx-bus-width: - const: 1 + items: + - const: 1 spi-tx-bus-width: - const: 1 + items: + - const: 1 required: - compatible diff --git a/Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml b/Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml index 78093468dd5e..8e441742cee6 100644 --- a/Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml +++ b/Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml @@ -45,10 +45,12 @@ patternProperties: properties: spi-rx-bus-width: - enum: [1, 4] + items: + - enum: [1, 4] spi-tx-bus-width: - enum: [1, 4] + items: + - enum: [1, 4] allOf: - $ref: spi-controller.yaml# diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml b/Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml index 8b3640280559..909c204b8adf 100644 --- a/Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml @@ -54,10 +54,12 @@ patternProperties: properties: spi-rx-bus-width: - enum: [1, 2, 4] + items: + - enum: [1, 2, 4] spi-tx-bus-width: - enum: [1, 2, 4] + items: + - enum: [1, 2, 4] required: - compatible diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml index 8b6e8fc009db..880a9f624566 100644 --- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml +++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml @@ -64,9 +64,23 @@ properties: description: Bus width to the SPI bus used for read transfers. If 0 is provided, then no RX will be possible on this device. - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2, 4, 8] - default: 1 + + Some SPI peripherals and controllers may have multiple data lanes for + receiving two or more words at the same time. If this is the case, each + index in the array represents the lane on both the SPI peripheral and + controller. Additional mapping properties may be needed if a lane is + skipped on either side. + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + enum: [0, 1, 2, 4, 8] + default: [1] + + spi-rx-lane-map: + description: Mapping of peripheral SDO lanes to controller SDI lanes. + Each index in the array represents a peripheral SDO lane, and the value + at that index represents the corresponding controller SDI lane. + $ref: /schemas/types.yaml#/definitions/uint32-array + default: [0, 1, 2, 3, 4, 5, 6, 7] spi-rx-delay-us: description: @@ -81,9 +95,23 @@ properties: description: Bus width to the SPI bus used for write transfers. If 0 is provided, then no TX will be possible on this device. - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2, 4, 8] - default: 1 + + Some SPI peripherals and controllers may have multiple data lanes for + transmitting two or more words at the same time. If this is the case, each + index in the array represents the lane on both the SPI peripheral and + controller. Additional mapping properties may be needed if a lane is + skipped on either side. + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + enum: [0, 1, 2, 4, 8] + default: [1] + + spi-tx-lane-map: + description: Mapping of peripheral SDI lanes to controller SDO lanes. + Each index in the array represents a peripheral SDI lane, and the value + at that index represents the corresponding controller SDO lane. + $ref: /schemas/types.yaml#/definitions/uint32-array + default: [0, 1, 2, 3, 4, 5, 6, 7] spi-tx-delay-us: description: diff --git a/Documentation/spi/index.rst b/Documentation/spi/index.rst index 824ce42ed4f0..2c89b1ee39e2 100644 --- a/Documentation/spi/index.rst +++ b/Documentation/spi/index.rst @@ -9,6 +9,7 @@ Serial Peripheral Interface (SPI) spi-summary spidev + multiple-data-lanes butterfly spi-lm70llp spi-sc18is602 diff --git a/Documentation/spi/multiple-data-lanes.rst b/Documentation/spi/multiple-data-lanes.rst new file mode 100644 index 000000000000..69cb532d052f --- /dev/null +++ b/Documentation/spi/multiple-data-lanes.rst @@ -0,0 +1,217 @@ +==================================== +SPI devices with multiple data lanes +==================================== + +Some specialized SPI controllers and peripherals support multiple data lanes +that allow reading more than one word at a time in parallel. This is different +from dual/quad/octal SPI where multiple bits of a single word are transferred +simultaneously. + +For example, controllers that support parallel flash memories have this feature +as do some simultaneous-sampling ADCs where each channel has its own data lane. + +--------------------- +Describing the wiring +--------------------- + +The ``spi-tx-bus-width`` and ``spi-rx-bus-width`` properties in the devicetree +are used to describe how many data lanes are connected between the controller +and how wide each lane is. The number of items in the array indicates how many +lanes there are, and the value of each item indicates how many bits wide that +lane is. + +For example, a dual-simultaneous-sampling ADC with two 4-bit lanes might be +wired up like this:: + + +--------------+ +----------+ + | SPI | | AD4630 | + | Controller | | ADC | + | | | | + | CS0 |--->| CS | + | SCK |--->| SCK | + | SDO |--->| SDI | + | | | | + | SDIA0 |<---| SDOA0 | + | SDIA1 |<---| SDOA1 | + | SDIA2 |<---| SDOA2 | + | SDIA3 |<---| SDOA3 | + | | | | + | SDIB0 |<---| SDOB0 | + | SDIB1 |<---| SDOB1 | + | SDIB2 |<---| SDOB2 | + | SDIB3 |<---| SDOB3 | + | | | | + +--------------+ +----------+ + +It is described in a devicetree like this:: + + spi { + compatible = "my,spi-controller"; + + ... + + adc@0 { + compatible = "adi,ad4630"; + reg = <0>; + ... + spi-rx-bus-width = <4>, <4>; /* 2 lanes of 4 bits each */ + ... + }; + }; + +In most cases, lanes will be wired up symmetrically (A to A, B to B, etc). If +this isn't the case, extra ``spi-rx-lane-map`` and ``spi-tx-lane-map`` +properties are needed to provide a mapping between controller lanes and the +physical lane wires. + +Here is an example where a multi-lane SPI controller has each lane wired to +separate single-lane peripherals:: + + +--------------+ +----------+ + | SPI | | Thing 1 | + | Controller | | | + | | | | + | CS0 |--->| CS | + | SDO0 |--->| SDI | + | SDI0 |<---| SDO | + | SCLK0 |--->| SCLK | + | | | | + | | +----------+ + | | + | | +----------+ + | | | Thing 2 | + | | | | + | CS1 |--->| CS | + | SDO1 |--->| SDI | + | SDI1 |<---| SDO | + | SCLK1 |--->| SCLK | + | | | | + +--------------+ +----------+ + +This is described in a devicetree like this:: + + spi { + compatible = "my,spi-controller"; + + ... + + thing1@0 { + compatible = "my,thing1"; + reg = <0>; + ... + }; + + thing2@1 { + compatible = "my,thing2"; + reg = <1>; + ... + spi-tx-lane-map = <1>; /* lane 0 is not used, lane 1 is used for tx wire */ + spi-rx-lane-map = <1>; /* lane 0 is not used, lane 1 is used for rx wire */ + ... + }; + }; + + +The default values of ``spi-rx-bus-width`` and ``spi-tx-bus-width`` are ``<1>``, +so these properties can still be omitted even when ``spi-rx-lane-map`` and +``spi-tx-lane-map`` are used. + +---------------------------- +Usage in a peripheral driver +---------------------------- + +These types of SPI controllers generally do not support arbitrary use of the +multiple lanes. Instead, they operate in one of a few defined modes. Peripheral +drivers should set the :c:type:`struct spi_transfer.multi_lane_mode <spi_transfer>` +field to indicate which mode they want to use for a given transfer. + +The possible values for this field have the following semantics: + +- :c:macro:`SPI_MULTI_BUS_MODE_SINGLE`: Only use the first lane. Other lanes are + ignored. This means that it is operating just like a conventional SPI + peripheral. This is the default, so it does not need to be explicitly set. + + Example:: + + tx_buf[0] = 0x88; + + struct spi_transfer xfer = { + .tx_buf = tx_buf, + .len = 1, + }; + + spi_sync_transfer(spi, &xfer, 1); + + Assuming the controller is sending the MSB first, the sequence of bits + sent over the tx wire would be (right-most bit is sent first):: + + controller > data bits > peripheral + ---------- ---------------- ---------- + SDO 0 0-0-0-1-0-0-0-1 SDI 0 + +- :c:macro:`SPI_MULTI_BUS_MODE_MIRROR`: Send a single data word over all of the + lanes at the same time. This only makes sense for writes and not + for reads. + + Example:: + + tx_buf[0] = 0x88; + + struct spi_transfer xfer = { + .tx_buf = tx_buf, + .len = 1, + .multi_lane_mode = SPI_MULTI_BUS_MODE_MIRROR, + }; + + spi_sync_transfer(spi, &xfer, 1); + + The data is mirrored on each tx wire:: + + controller > data bits > peripheral + ---------- ---------------- ---------- + SDO 0 0-0-0-1-0-0-0-1 SDI 0 + SDO 1 0-0-0-1-0-0-0-1 SDI 1 + +- :c:macro:`SPI_MULTI_BUS_MODE_STRIPE`: Send or receive two different data words + at the same time, one on each lane. This means that the buffer needs to be + sized to hold data for all lanes. Data is interleaved in the buffer, with + the first word corresponding to lane 0, the second to lane 1, and so on. + Once the last lane is used, the next word in the buffer corresponds to lane + 0 again. Accordingly, the buffer size must be a multiple of the number of + lanes. This mode works for both reads and writes. + + Example:: + + struct spi_transfer xfer = { + .rx_buf = rx_buf, + .len = 2, + .multi_lane_mode = SPI_MULTI_BUS_MODE_STRIPE, + }; + + spi_sync_transfer(spi, &xfer, 1); + + Each rx wire has a different data word sent simultaneously:: + + controller < data bits < peripheral + ---------- ---------------- ---------- + SDI 0 0-0-0-1-0-0-0-1 SDO 0 + SDI 1 1-0-0-0-1-0-0-0 SDO 1 + + After the transfer, ``rx_buf[0] == 0x11`` (word from SDO 0) and + ``rx_buf[1] == 0x88`` (word from SDO 1). + + +----------------------------- +SPI controller driver support +----------------------------- + +To support multiple data lanes, SPI controller drivers need to set +:c:type:`struct spi_controller.num_data_lanes <spi_controller>` to a value +greater than 1. + +Then the part of the driver that handles SPI transfers needs to check the +:c:type:`struct spi_transfer.multi_lane_mode <spi_transfer>` field and implement +the appropriate behavior for each supported mode and return an error for +unsupported modes. + +The core SPI code should handle the rest. diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 91805eae9263..c75e8da049f7 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -23,6 +23,9 @@ #include <linux/spi/spi.h> #include <trace/events/spi.h> +#define SPI_ENGINE_REG_DATA_WIDTH 0x0C +#define SPI_ENGINE_REG_DATA_WIDTH_NUM_OF_SDIO_MASK GENMASK(23, 16) +#define SPI_ENGINE_REG_DATA_WIDTH_MASK GENMASK(15, 0) #define SPI_ENGINE_REG_OFFLOAD_MEM_ADDR_WIDTH 0x10 #define SPI_ENGINE_REG_RESET 0x40 @@ -75,6 +78,8 @@ #define SPI_ENGINE_CMD_REG_CLK_DIV 0x0 #define SPI_ENGINE_CMD_REG_CONFIG 0x1 #define SPI_ENGINE_CMD_REG_XFER_BITS 0x2 +#define SPI_ENGINE_CMD_REG_SDI_MASK 0x3 +#define SPI_ENGINE_CMD_REG_SDO_MASK 0x4 #define SPI_ENGINE_MISC_SYNC 0x0 #define SPI_ENGINE_MISC_SLEEP 0x1 @@ -105,6 +110,10 @@ #define SPI_ENGINE_OFFLOAD_CMD_FIFO_SIZE 16 #define SPI_ENGINE_OFFLOAD_SDO_FIFO_SIZE 16 +/* Extending SPI_MULTI_LANE_MODE values for optimizing messages. */ +#define SPI_ENGINE_MULTI_BUS_MODE_UNKNOWN -1 +#define SPI_ENGINE_MULTI_BUS_MODE_CONFLICTING -2 + struct spi_engine_program { unsigned int length; uint16_t instructions[] __counted_by(length); @@ -142,6 +151,11 @@ struct spi_engine_offload { unsigned long flags; unsigned int offload_num; unsigned int spi_mode_config; + unsigned int multi_lane_mode; + u8 rx_primary_lane_mask; + u8 tx_primary_lane_mask; + u8 rx_all_lanes_mask; + u8 tx_all_lanes_mask; u8 bits_per_word; }; @@ -165,6 +179,25 @@ struct spi_engine { bool offload_requires_sync; }; +static void spi_engine_primary_lane_flag(struct spi_device *spi, + u8 *rx_lane_flags, u8 *tx_lane_flags) +{ + *rx_lane_flags = BIT(spi->rx_lane_map[0]); + *tx_lane_flags = BIT(spi->tx_lane_map[0]); +} + +static void spi_engine_all_lanes_flags(struct spi_device *spi, + u8 *rx_lane_flags, u8 *tx_lane_flags) +{ + int i; + + for (i = 0; i < spi->num_rx_lanes; i++) + *rx_lane_flags |= BIT(spi->rx_lane_map[i]); + + for (i = 0; i < spi->num_tx_lanes; i++) + *tx_lane_flags |= BIT(spi->tx_lane_map[i]); +} + static void spi_engine_program_add_cmd(struct spi_engine_program *p, bool dry, uint16_t cmd) { @@ -193,7 +226,7 @@ static unsigned int spi_engine_get_config(struct spi_device *spi) } static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry, - struct spi_transfer *xfer) + struct spi_transfer *xfer, u32 num_lanes) { unsigned int len; @@ -204,6 +237,9 @@ static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry, else len = xfer->len / 4; + if (xfer->multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE) + len /= num_lanes; + while (len) { unsigned int n = min(len, 256U); unsigned int flags = 0; @@ -269,6 +305,7 @@ static int spi_engine_precompile_message(struct spi_message *msg) { unsigned int clk_div, max_hz = msg->spi->controller->max_speed_hz; struct spi_transfer *xfer; + int multi_lane_mode = SPI_ENGINE_MULTI_BUS_MODE_UNKNOWN; u8 min_bits_per_word = U8_MAX; u8 max_bits_per_word = 0; @@ -284,6 +321,24 @@ static int spi_engine_precompile_message(struct spi_message *msg) min_bits_per_word = min(min_bits_per_word, xfer->bits_per_word); max_bits_per_word = max(max_bits_per_word, xfer->bits_per_word); } + + if (xfer->rx_buf || xfer->offload_flags & SPI_OFFLOAD_XFER_RX_STREAM || + xfer->tx_buf || xfer->offload_flags & SPI_OFFLOAD_XFER_TX_STREAM) { + switch (xfer->multi_lane_mode) { + case SPI_MULTI_LANE_MODE_SINGLE: + case SPI_MULTI_LANE_MODE_STRIPE: + break; + default: + /* Other modes, like mirror not supported */ + return -EINVAL; + } + + /* If all xfers have the same multi-lane mode, we can optimize. */ + if (multi_lane_mode == SPI_ENGINE_MULTI_BUS_MODE_UNKNOWN) + multi_lane_mode = xfer->multi_lane_mode; + else if (multi_lane_mode != xfer->multi_lane_mode) + multi_lane_mode = SPI_ENGINE_MULTI_BUS_MODE_CONFLICTING; + } } /* @@ -297,6 +352,14 @@ static int spi_engine_precompile_message(struct spi_message *msg) priv->bits_per_word = min_bits_per_word; else priv->bits_per_word = 0; + + priv->multi_lane_mode = multi_lane_mode; + spi_engine_primary_lane_flag(msg->spi, + &priv->rx_primary_lane_mask, + &priv->tx_primary_lane_mask); + spi_engine_all_lanes_flags(msg->spi, + &priv->rx_all_lanes_mask, + &priv->tx_all_lanes_mask); } return 0; @@ -310,6 +373,7 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, struct spi_engine_offload *priv; struct spi_transfer *xfer; int clk_div, new_clk_div, inst_ns; + int prev_multi_lane_mode = SPI_MULTI_LANE_MODE_SINGLE; bool keep_cs = false; u8 bits_per_word = 0; @@ -334,6 +398,7 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, * in the same way. */ bits_per_word = priv->bits_per_word; + prev_multi_lane_mode = priv->multi_lane_mode; } else { spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG, @@ -344,6 +409,28 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, spi_engine_gen_cs(p, dry, spi, !xfer->cs_off); list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (xfer->rx_buf || xfer->offload_flags & SPI_OFFLOAD_XFER_RX_STREAM || + xfer->tx_buf || xfer->offload_flags & SPI_OFFLOAD_XFER_TX_STREAM) { + if (xfer->multi_lane_mode != prev_multi_lane_mode) { + u8 tx_lane_flags, rx_lane_flags; + + if (xfer->multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE) + spi_engine_all_lanes_flags(spi, &rx_lane_flags, + &tx_lane_flags); + else + spi_engine_primary_lane_flag(spi, &rx_lane_flags, + &tx_lane_flags); + + spi_engine_program_add_cmd(p, dry, + SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK, + rx_lane_flags)); + spi_engine_program_add_cmd(p, dry, + SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK, + tx_lane_flags)); + } + prev_multi_lane_mode = xfer->multi_lane_mode; + } + new_clk_div = host->max_speed_hz / xfer->effective_speed_hz; if (new_clk_div != clk_div) { clk_div = new_clk_div; @@ -360,7 +447,7 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, bits_per_word)); } - spi_engine_gen_xfer(p, dry, xfer); + spi_engine_gen_xfer(p, dry, xfer, spi->num_rx_lanes); spi_engine_gen_sleep(p, dry, spi_delay_to_ns(&xfer->delay, xfer), inst_ns, xfer->effective_speed_hz); @@ -394,6 +481,19 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, if (clk_div != 1) spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV, 0)); + + /* Restore single lane mode unless offload disable will restore it later. */ + if (prev_multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE && + (!msg->offload || priv->multi_lane_mode != SPI_MULTI_LANE_MODE_STRIPE)) { + u8 rx_lane_flags, tx_lane_flags; + + spi_engine_primary_lane_flag(spi, &rx_lane_flags, &tx_lane_flags); + + spi_engine_program_add_cmd(p, dry, + SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK, rx_lane_flags)); + spi_engine_program_add_cmd(p, dry, + SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK, tx_lane_flags)); + } } static void spi_engine_xfer_next(struct spi_message *msg, @@ -799,6 +899,19 @@ static int spi_engine_setup(struct spi_device *device) writel_relaxed(SPI_ENGINE_CMD_CS_INV(spi_engine->cs_inv), spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + if (host->num_data_lanes > 1) { + u8 rx_lane_flags, tx_lane_flags; + + spi_engine_primary_lane_flag(device, &rx_lane_flags, &tx_lane_flags); + + writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK, + rx_lane_flags), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK, + tx_lane_flags), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + } + /* * In addition to setting the flags, we have to do a CS assert command * to make the new setting actually take effect. @@ -902,6 +1015,15 @@ static int spi_engine_trigger_enable(struct spi_offload *offload) priv->bits_per_word), spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + if (priv->multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE) { + writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK, + priv->rx_all_lanes_mask), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK, + priv->tx_all_lanes_mask), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + } + writel_relaxed(SPI_ENGINE_CMD_SYNC(1), spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); @@ -929,6 +1051,16 @@ static void spi_engine_trigger_disable(struct spi_offload *offload) reg &= ~SPI_ENGINE_OFFLOAD_CTRL_ENABLE; writel_relaxed(reg, spi_engine->base + SPI_ENGINE_REG_OFFLOAD_CTRL(priv->offload_num)); + + /* Restore single-lane mode. */ + if (priv->multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE) { + writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK, + priv->rx_primary_lane_mask), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK, + priv->tx_primary_lane_mask), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + } } static struct dma_chan @@ -973,7 +1105,7 @@ static int spi_engine_probe(struct platform_device *pdev) { struct spi_engine *spi_engine; struct spi_controller *host; - unsigned int version; + unsigned int version, data_width_reg_val; int irq, ret; irq = platform_get_irq(pdev, 0); @@ -1042,7 +1174,7 @@ static int spi_engine_probe(struct platform_device *pdev) return PTR_ERR(spi_engine->base); version = readl(spi_engine->base + ADI_AXI_REG_VERSION); - if (ADI_AXI_PCORE_VER_MAJOR(version) != 1) { + if (ADI_AXI_PCORE_VER_MAJOR(version) > 2) { dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%u\n", ADI_AXI_PCORE_VER_MAJOR(version), ADI_AXI_PCORE_VER_MINOR(version), @@ -1050,6 +1182,8 @@ static int spi_engine_probe(struct platform_device *pdev) return -ENODEV; } + data_width_reg_val = readl(spi_engine->base + SPI_ENGINE_REG_DATA_WIDTH); + if (adi_axi_pcore_ver_gteq(version, 1, 1)) { unsigned int sizes = readl(spi_engine->base + SPI_ENGINE_REG_OFFLOAD_MEM_ADDR_WIDTH); @@ -1096,6 +1230,9 @@ static int spi_engine_probe(struct platform_device *pdev) } if (adi_axi_pcore_ver_gteq(version, 1, 3)) host->mode_bits |= SPI_MOSI_IDLE_LOW | SPI_MOSI_IDLE_HIGH; + if (adi_axi_pcore_ver_gteq(version, 2, 0)) + host->num_data_lanes = FIELD_GET(SPI_ENGINE_REG_DATA_WIDTH_NUM_OF_SDIO_MASK, + data_width_reg_val); if (host->max_speed_hz == 0) return dev_err_probe(&pdev->dev, -EINVAL, "spi_clk rate is 0"); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b773c297f8b1..a24de5c98399 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2354,8 +2354,8 @@ static void of_spi_parse_dt_cs_delay(struct device_node *nc, static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, struct device_node *nc) { - u32 value, cs[SPI_DEVICE_CS_CNT_MAX]; - int rc, idx; + u32 value, cs[SPI_DEVICE_CS_CNT_MAX], map[SPI_DEVICE_DATA_LANE_CNT_MAX]; + int rc, idx, max_num_data_lanes; /* Mode (clock phase/polarity/etc.) */ if (of_property_read_bool(nc, "spi-cpha")) @@ -2370,7 +2370,65 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, spi->mode |= SPI_CS_HIGH; /* Device DUAL/QUAD mode */ - if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) { + + rc = of_property_read_variable_u32_array(nc, "spi-tx-lane-map", map, 1, + ARRAY_SIZE(map)); + if (rc >= 0) { + max_num_data_lanes = rc; + for (idx = 0; idx < max_num_data_lanes; idx++) + spi->tx_lane_map[idx] = map[idx]; + } else if (rc == -EINVAL) { + /* Default lane map is identity mapping. */ + max_num_data_lanes = ARRAY_SIZE(spi->tx_lane_map); + for (idx = 0; idx < max_num_data_lanes; idx++) + spi->tx_lane_map[idx] = idx; + } else { + dev_err(&ctlr->dev, + "failed to read spi-tx-lane-map property: %d\n", rc); + return rc; + } + + rc = of_property_count_u32_elems(nc, "spi-tx-bus-width"); + if (rc < 0 && rc != -EINVAL) { + dev_err(&ctlr->dev, + "failed to read spi-tx-bus-width property: %d\n", rc); + return rc; + } + if (rc > max_num_data_lanes) { + dev_err(&ctlr->dev, + "spi-tx-bus-width has more elements (%d) than spi-tx-lane-map (%d)\n", + rc, max_num_data_lanes); + return -EINVAL; + } + + if (rc == -EINVAL) { + /* Default when property is not present. */ + spi->num_tx_lanes = 1; + } else { + u32 first_value; + + spi->num_tx_lanes = rc; + + for (idx = 0; idx < spi->num_tx_lanes; idx++) { + rc = of_property_read_u32_index(nc, "spi-tx-bus-width", + idx, &value); + if (rc) + return rc; + + /* + * For now, we only support all lanes having the same + * width so we can keep using the existing mode flags. + */ + if (!idx) + first_value = value; + else if (first_value != value) { + dev_err(&ctlr->dev, + "spi-tx-bus-width has inconsistent values: first %d vs later %d\n", + first_value, value); + return -EINVAL; + } + } + switch (value) { case 0: spi->mode |= SPI_NO_TX; @@ -2394,7 +2452,74 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, } } - if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) { + for (idx = 0; idx < spi->num_tx_lanes; idx++) { + if (spi->tx_lane_map[idx] >= spi->controller->num_data_lanes) { + dev_err(&ctlr->dev, + "spi-tx-lane-map has invalid value %d (num_data_lanes=%d)\n", + spi->tx_lane_map[idx], + spi->controller->num_data_lanes); + return -EINVAL; + } + } + + rc = of_property_read_variable_u32_array(nc, "spi-rx-lane-map", map, 1, + ARRAY_SIZE(map)); + if (rc >= 0) { + max_num_data_lanes = rc; + for (idx = 0; idx < max_num_data_lanes; idx++) + spi->rx_lane_map[idx] = map[idx]; + } else if (rc == -EINVAL) { + /* Default lane map is identity mapping. */ + max_num_data_lanes = ARRAY_SIZE(spi->rx_lane_map); + for (idx = 0; idx < max_num_data_lanes; idx++) + spi->rx_lane_map[idx] = idx; + } else { + dev_err(&ctlr->dev, + "failed to read spi-rx-lane-map property: %d\n", rc); + return rc; + } + + rc = of_property_count_u32_elems(nc, "spi-rx-bus-width"); + if (rc < 0 && rc != -EINVAL) { + dev_err(&ctlr->dev, + "failed to read spi-rx-bus-width property: %d\n", rc); + return rc; + } + if (rc > max_num_data_lanes) { + dev_err(&ctlr->dev, + "spi-rx-bus-width has more elements (%d) than spi-rx-lane-map (%d)\n", + rc, max_num_data_lanes); + return -EINVAL; + } + + if (rc == -EINVAL) { + /* Default when property is not present. */ + spi->num_rx_lanes = 1; + } else { + u32 first_value; + + spi->num_rx_lanes = rc; + + for (idx = 0; idx < spi->num_rx_lanes; idx++) { + rc = of_property_read_u32_index(nc, "spi-rx-bus-width", + idx, &value); + if (rc) + return rc; + + /* + * For now, we only support all lanes having the same + * width so we can keep using the existing mode flags. + */ + if (!idx) + first_value = value; + else if (first_value != value) { + dev_err(&ctlr->dev, + "spi-rx-bus-width has inconsistent values: first %d vs later %d\n", + first_value, value); + return -EINVAL; + } + } + switch (value) { case 0: spi->mode |= SPI_NO_RX; @@ -2418,6 +2543,16 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, } } + for (idx = 0; idx < spi->num_rx_lanes; idx++) { + if (spi->rx_lane_map[idx] >= spi->controller->num_data_lanes) { + dev_err(&ctlr->dev, + "spi-rx-lane-map has invalid value %d (num_data_lanes=%d)\n", + spi->rx_lane_map[idx], + spi->controller->num_data_lanes); + return -EINVAL; + } + } + if (spi_controller_is_target(ctlr)) { if (!of_node_name_eq(nc, "slave")) { dev_err(&ctlr->dev, "%pOF is not called 'slave'\n", @@ -3066,6 +3201,7 @@ struct spi_controller *__spi_alloc_controller(struct device *dev, mutex_init(&ctlr->add_lock); ctlr->bus_num = -1; ctlr->num_chipselect = 1; + ctlr->num_data_lanes = 1; ctlr->target = target; if (IS_ENABLED(CONFIG_SPI_SLAVE) && target) ctlr->dev.class = &spi_target_class; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index cb2c2df31089..39681f7e063b 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -23,6 +23,9 @@ /* Max no. of CS supported per spi device */ #define SPI_DEVICE_CS_CNT_MAX 4 +/* Max no. of data lanes supported per spi device */ +#define SPI_DEVICE_DATA_LANE_CNT_MAX 8 + struct dma_chan; struct software_node; struct ptp_system_timestamp; @@ -174,6 +177,10 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg, * @cs_index_mask: Bit mask of the active chipselect(s) in the chipselect array * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines * (optional, NULL when not using a GPIO line) + * @tx_lane_map: Map of peripheral lanes (index) to controller lanes (value). + * @num_tx_lanes: Number of transmit lanes wired up. + * @rx_lane_map: Map of peripheral lanes (index) to controller lanes (value). + * @num_rx_lanes: Number of receive lanes wired up. * * A @spi_device is used to interchange data between an SPI target device * (usually a discrete chip) and CPU memory. @@ -242,6 +249,12 @@ struct spi_device { struct gpio_desc *cs_gpiod[SPI_DEVICE_CS_CNT_MAX]; /* Chip select gpio desc */ + /* Multi-lane SPI controller support. */ + u8 tx_lane_map[SPI_DEVICE_DATA_LANE_CNT_MAX]; + u8 num_tx_lanes; + u8 rx_lane_map[SPI_DEVICE_DATA_LANE_CNT_MAX]; + u8 num_rx_lanes; + /* * Likely need more hooks for more protocol options affecting how * the controller talks to each chip, like: @@ -401,6 +414,7 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch * SPI targets, and are numbered from zero to num_chipselects. * each target has a chipselect signal, but it's common that not * every chipselect is connected to a target. + * @num_data_lanes: Number of data lanes supported by this controller. Default is 1. * @dma_alignment: SPI controller constraint on DMA buffers alignment. * @mode_bits: flags understood by this controller driver * @buswidth_override_bits: flags to override for this controller driver @@ -576,6 +590,14 @@ struct spi_controller { */ u16 num_chipselect; + /* + * Some specialized SPI controllers can have more than one physical + * data lane interface per controller (each having it's own serializer). + * This specifies the number of data lanes in that case. Other + * controllers do not need to set this (defaults to 1). + */ + u16 num_data_lanes; + /* Some SPI controllers pose alignment requirements on DMAable * buffers; let protocol drivers know about these requirements. */ @@ -959,6 +981,8 @@ struct spi_res { * (SPI_NBITS_SINGLE) is used. * @rx_nbits: number of bits used for reading. If 0 the default * (SPI_NBITS_SINGLE) is used. + * @multi_lane_mode: How to serialize data on multiple lanes. One of the + * SPI_MULTI_LANE_MODE_* values. * @len: size of rx and tx buffers (in bytes) * @speed_hz: Select a speed other than the device default for this * transfer. If 0 the default (from @spi_device) is used. @@ -1095,6 +1119,12 @@ struct spi_transfer { unsigned cs_change:1; unsigned tx_nbits:4; unsigned rx_nbits:4; + +#define SPI_MULTI_LANE_MODE_SINGLE 0 /* only use single lane */ +#define SPI_MULTI_LANE_MODE_STRIPE 1 /* one data word per lane */ +#define SPI_MULTI_LANE_MODE_MIRROR 2 /* same word sent on all lanes */ + unsigned multi_lane_mode: 2; + unsigned timestamped:1; bool dtr_mode; #define SPI_NBITS_SINGLE 0x01 /* 1-bit transfer */ |
