]> Git Repo - J-linux.git/commitdiff
spi: sun6i: fix RX data corruption in DMA mode
authorMark Brown <[email protected]>
Mon, 4 Sep 2023 14:53:37 +0000 (15:53 +0100)
committerMark Brown <[email protected]>
Mon, 4 Sep 2023 14:53:37 +0000 (15:53 +0100)
Merge series from Tobias Schramm <[email protected]>:

This set of patches fixes two bugs in the sun6i SPI driver that result in
corruption of received data in DMA RX mode.

The first bug seems to be related to an incompatibility of the SPI RX FIFO
with wider than single byte read accesses during SPI transfers. I'm not
sure if this bug affects all types of SPI controllers found in Allwinner
SoCs supported by this driver. However reducing the access width should
always be safe. I've tested this change on a V3s SoC. Further testing to
narrow down the set of affected SoCs in the future would be welcome.

The second bug is a race between SPI RX DMA and FIFO drain logic for
interrupt-based SPI operation. This bug affects all SPI controllers
supported by this driver. Once again this change has been tested on the
Allwinner V3s SoC.

1  2 
MAINTAINERS
drivers/spi/spi-cadence.c
drivers/spi/spi-stm32.c
drivers/spi/spi-sun6i.c

diff --combined MAINTAINERS
index 4e0f5313102da5be6534ecc806b9ba28f550ac8c,4cc6bf79fdd832def1b24def9646996c00bcc619..8d8d59b17c0eb12b6168037ca748f2c550ca4012
@@@ -4126,7 -4126,7 +4126,7 @@@ BROADCOM BCM6348/BCM6358 SPI controlle
  M:    Jonas Gorski <[email protected]>
  L:    [email protected]
  S:    Odd Fixes
 -F:    Documentation/devicetree/bindings/spi/spi-bcm63xx.txt
 +F:    Documentation/devicetree/bindings/spi/brcm,bcm63xx-spi.yaml
  F:    drivers/spi/spi-bcm63xx.c
  
  BROADCOM ETHERNET PHY DRIVERS
@@@ -4886,11 -4886,7 +4886,11 @@@ L:    [email protected] (moderat
  L:    [email protected]
  S:    Maintained
  F:    Documentation/devicetree/bindings/sound/cirrus,cs*
 +F:    drivers/mfd/cs42l43*
 +F:    drivers/pinctrl/cirrus/pinctrl-cs42l43*
 +F:    drivers/spi/spi-cs42l43*
  F:    include/dt-bindings/sound/cs*
 +F:    include/linux/mfd/cs42l43*
  F:    include/sound/cs*
  F:    sound/pci/hda/cs*
  F:    sound/pci/hda/hda_cs_dsp_ctl.*
@@@ -8816,6 -8812,7 +8816,7 @@@ R:      Michael Walle <[email protected]
  S:    Maintained
  F:    drivers/gpio/gpio-regmap.c
  F:    include/linux/gpio/regmap.h
+ K:    (devm_)?gpio_regmap_(un)?register
  
  GPIO SUBSYSTEM
  M:    Linus Walleij <[email protected]>
@@@ -12273,16 -12270,6 +12274,16 @@@ F: Documentation/devicetree/bindings/cl
  F:    drivers/clk/clk-loongson2.c
  F:    include/dt-bindings/clock/loongson,ls2k-clk.h
  
 +LOONGSON SPI DRIVER
 +M:    Yinbo Zhu <[email protected]>
 +L:    [email protected]
 +S:    Maintained
 +F:    Documentation/devicetree/bindings/spi/loongson,ls2k-spi.yaml
 +F:    drivers/spi/spi-loongson-core.c
 +F:    drivers/spi/spi-loongson-pci.c
 +F:    drivers/spi/spi-loongson-plat.c
 +F:    drivers/spi/spi-loongson.h
 +
  LOONGSON-2 SOC SERIES GUTS DRIVER
  M:    Yinbo Zhu <[email protected]>
  L:    [email protected]
@@@ -14816,6 -14803,16 +14817,16 @@@ F: net/netfilter/xt_CONNSECMARK.
  F:    net/netfilter/xt_SECMARK.c
  F:    net/netlabel/
  
+ NETWORKING [MACSEC]
+ M:    Sabrina Dubroca <[email protected]>
+ L:    [email protected]
+ S:    Maintained
+ F:    drivers/net/macsec.c
+ F:    include/net/macsec.h
+ F:    include/uapi/linux/if_macsec.h
+ K:    macsec
+ K:    \bmdo_
  NETWORKING [MPTCP]
  M:    Matthieu Baerts <[email protected]>
  M:    Mat Martineau <[email protected]>
@@@ -19237,13 -19234,6 +19248,6 @@@ F:  Documentation/devicetree/bindings/se
  F:    drivers/tty/serdev/
  F:    include/linux/serdev.h
  
- SERIAL DRIVERS
- M:    Greg Kroah-Hartman <[email protected]>
- L:    [email protected]
- S:    Maintained
- F:    Documentation/devicetree/bindings/serial/
- F:    drivers/tty/serial/
  SERIAL IR RECEIVER
  M:    Sean Young <[email protected]>
  L:    [email protected]
@@@ -21072,6 -21062,39 +21076,39 @@@ S: Maintaine
  F:    Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml
  F:    sound/soc/ti/
  
+ TEXAS INSTRUMENTS AUDIO (ASoC/HDA) DRIVERS
+ M:    Shenghao Ding <[email protected]>
+ M:    Kevin Lu <[email protected]>
+ M:    Baojun Xu <[email protected]>
+ L:    [email protected] (moderated for non-subscribers)
+ S:    Maintained
+ F:    Documentation/devicetree/bindings/sound/tas2552.txt
+ F:    Documentation/devicetree/bindings/sound/tas2562.yaml
+ F:    Documentation/devicetree/bindings/sound/tas2770.yaml
+ F:    Documentation/devicetree/bindings/sound/tas27xx.yaml
+ F:    Documentation/devicetree/bindings/sound/ti,pcm1681.txt
+ F:    Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml
+ F:    Documentation/devicetree/bindings/sound/ti,tlv320*.yaml
+ F:    Documentation/devicetree/bindings/sound/tlv320adcx140.yaml
+ F:    Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
+ F:    Documentation/devicetree/bindings/sound/tpa6130a2.txt
+ F:    include/sound/tas2*.h
+ F:    include/sound/tlv320*.h
+ F:    include/sound/tpa6130a2-plat.h
+ F:    sound/pci/hda/tas2781_hda_i2c.c
+ F:    sound/soc/codecs/pcm1681.c
+ F:    sound/soc/codecs/pcm1789*.*
+ F:    sound/soc/codecs/pcm179x*.*
+ F:    sound/soc/codecs/pcm186x*.*
+ F:    sound/soc/codecs/pcm3008.*
+ F:    sound/soc/codecs/pcm3060*.*
+ F:    sound/soc/codecs/pcm3168a*.*
+ F:    sound/soc/codecs/pcm5102a.c
+ F:    sound/soc/codecs/pcm512x*.*
+ F:    sound/soc/codecs/tas2*.*
+ F:    sound/soc/codecs/tlv320*.*
+ F:    sound/soc/codecs/tpa6130a2.*
  TEXAS INSTRUMENTS DMA DRIVERS
  M:    Peter Ujfalusi <[email protected]>
  L:    [email protected]
@@@ -21648,20 -21671,16 +21685,16 @@@ W:        https://github.com/srcres258/linux-d
  T:    git git://github.com/srcres258/linux-doc.git doc-zh-tw
  F:    Documentation/translations/zh_TW/
  
- TTY LAYER
+ TTY LAYER AND SERIAL DRIVERS
  M:    Greg Kroah-Hartman <[email protected]>
  M:    Jiri Slaby <[email protected]>
  L:    [email protected]
  L:    [email protected]
  S:    Supported
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
+ F:    Documentation/devicetree/bindings/serial/
  F:    Documentation/driver-api/serial/
  F:    drivers/tty/
- F:    drivers/tty/serial/serial_base.h
- F:    drivers/tty/serial/serial_base_bus.c
- F:    drivers/tty/serial/serial_core.c
- F:    drivers/tty/serial/serial_ctrl.c
- F:    drivers/tty/serial/serial_port.c
  F:    include/linux/selection.h
  F:    include/linux/serial.h
  F:    include/linux/serial_core.h
  S:    Maintained
  F:    drivers/block/virtio_blk.c
  F:    drivers/scsi/virtio_scsi.c
- F:    drivers/vhost/scsi.c
  F:    include/uapi/linux/virtio_blk.h
  F:    include/uapi/linux/virtio_scsi.h
  
@@@ -22587,6 -22605,16 +22619,16 @@@ F: include/linux/vhost_iotlb.
  F:    include/uapi/linux/vhost.h
  F:    kernel/vhost_task.c
  
+ VIRTIO HOST (VHOST-SCSI)
+ M:    "Michael S. Tsirkin" <[email protected]>
+ M:    Jason Wang <[email protected]>
+ M:    Mike Christie <[email protected]>
+ R:    Paolo Bonzini <[email protected]>
+ R:    Stefan Hajnoczi <[email protected]>
+ L:    [email protected]
+ S:    Maintained
+ F:    drivers/vhost/scsi.c
  VIRTIO I2C DRIVER
  M:    Conghui Chen <[email protected]>
  M:    Viresh Kumar <[email protected]>
index 3c9813ad064bc712143b9c8cfab716a5ae1b8c77,9b021390263e8d28b1b49ed5b54f5a5ff4d2fffa..12c940ba074abd84384c7af06a84a1c21a411473
@@@ -1,6 -1,6 +1,6 @@@
  // SPDX-License-Identifier: GPL-2.0-or-later
  /*
 - * Cadence SPI controller driver (master and slave mode)
 + * Cadence SPI controller driver (host and target mode)
   *
   * Copyright (C) 2008 - 2014 Xilinx, Inc.
   *
                                        CDNS_SPI_CR_BAUD_DIV_4)
  
  /*
 - * SPI Configuration Register - Baud rate and slave select
 + * SPI Configuration Register - Baud rate and target select
   *
   * These are the values used in the calculation of baud rate divisor and
 - * setting the slave select.
 + * setting the target select.
   */
  
  #define CDNS_SPI_BAUD_DIV_MAX         7 /* Baud rate divisor maximum */
@@@ -141,20 -141,20 +141,20 @@@ static inline void cdns_spi_write(struc
  /**
   * cdns_spi_init_hw - Initialize the hardware and configure the SPI controller
   * @xspi:     Pointer to the cdns_spi structure
 - * @is_slave: Flag to indicate slave or master mode
 - * * On reset the SPI controller is configured to slave or  master mode.
 - * In master mode baud rate divisor is set to 4, threshold value for TX FIFO
 + * @is_target:        Flag to indicate target or host mode
 + * * On reset the SPI controller is configured to target or host mode.
 + * In host mode baud rate divisor is set to 4, threshold value for TX FIFO
   * not full interrupt is set to 1 and size of the word to be transferred as 8 bit.
   *
   * This function initializes the SPI controller to disable and clear all the
 - * interrupts, enable manual slave select and manual start, deselect all the
 + * interrupts, enable manual target select and manual start, deselect all the
   * chip select lines, and enable the SPI controller.
   */
 -static void cdns_spi_init_hw(struct cdns_spi *xspi, bool is_slave)
 +static void cdns_spi_init_hw(struct cdns_spi *xspi, bool is_target)
  {
        u32 ctrl_reg = 0;
  
 -      if (!is_slave)
 +      if (!is_target)
                ctrl_reg |= CDNS_SPI_CR_DEFAULT;
  
        if (xspi->is_decoded_cs)
@@@ -185,10 -185,10 +185,10 @@@ static void cdns_spi_chipselect(struct 
        ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR);
  
        if (is_high) {
 -              /* Deselect the slave */
 +              /* Deselect the target */
                ctrl_reg |= CDNS_SPI_CR_SSCTRL;
        } else {
 -              /* Select the slave */
 +              /* Select the target */
                ctrl_reg &= ~CDNS_SPI_CR_SSCTRL;
                if (!(xspi->is_decoded_cs))
                        ctrl_reg |= ((~(CDNS_SPI_SS0 << spi_get_chipselect(spi, 0))) <<
@@@ -227,7 -227,7 +227,7 @@@ static void cdns_spi_config_clock_mode(
                /*
                 * Just writing the CR register does not seem to apply the clock
                 * setting changes. This is problematic when changing the clock
 -               * polarity as it will cause the SPI slave to see spurious clock
 +               * polarity as it will cause the SPI target to see spurious clock
                 * transitions. To workaround the issue toggle the ER register.
                 */
                cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE);
@@@ -317,12 -317,6 +317,6 @@@ static void cdns_spi_process_fifo(struc
        xspi->rx_bytes -= nrx;
  
        while (ntx || nrx) {
-               /* When xspi in busy condition, bytes may send failed,
-                * then spi control did't work thoroughly, add one byte delay
-                */
-               if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
-                       udelay(10);
                if (ntx) {
                        if (xspi->txbuf)
                                cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
@@@ -392,6 -386,11 +386,11 @@@ static irqreturn_t cdns_spi_irq(int irq
                if (xspi->tx_bytes) {
                        cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt);
                } else {
+                       /* Fixed delay due to controller limitation with
+                        * RX_NEMPTY incorrect status
+                        * Xilinx AR:65885 contains more details
+                        */
+                       udelay(10);
                        cdns_spi_process_fifo(xspi, 0, trans_cnt);
                        cdns_spi_write(xspi, CDNS_SPI_IDR,
                                       CDNS_SPI_IXR_DEFAULT);
  static int cdns_prepare_message(struct spi_controller *ctlr,
                                struct spi_message *msg)
  {
 -      if (!spi_controller_is_slave(ctlr))
 +      if (!spi_controller_is_target(ctlr))
                cdns_spi_config_clock_mode(msg->spi);
        return 0;
  }
   * @transfer: Pointer to the spi_transfer structure which provides
   *            information about next transfer parameters
   *
 - * This function in master mode fills the TX FIFO, starts the SPI transfer and
 + * This function in host mode fills the TX FIFO, starts the SPI transfer and
   * returns a positive transfer count so that core will wait for completion.
 - * This function in slave mode fills the TX FIFO and wait for transfer trigger.
 + * This function in target mode fills the TX FIFO and wait for transfer trigger.
   *
   * Return:    Number of bytes transferred in the last transfer
   */
@@@ -435,16 -434,22 +434,22 @@@ static int cdns_transfer_one(struct spi
        xspi->tx_bytes = transfer->len;
        xspi->rx_bytes = transfer->len;
  
 -      if (!spi_controller_is_slave(ctlr)) {
 +      if (!spi_controller_is_target(ctlr)) {
                cdns_spi_setup_transfer(spi, transfer);
        } else {
                /* Set TX empty threshold to half of FIFO depth
-                * only if TX bytes are more than half FIFO depth.
+                * only if TX bytes are more than FIFO depth.
                 */
                if (xspi->tx_bytes > xspi->tx_fifo_depth)
                        cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1);
        }
  
+       /* When xspi in busy condition, bytes may send failed,
+        * then spi control didn't work thoroughly, add one byte delay
+        */
+       if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
+               udelay(10);
        cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
        spi_transfer_delay_exec(transfer);
  
   * @ctlr:     Pointer to the spi_controller structure which provides
   *            information about the controller.
   *
 - * This function enables SPI master controller.
 + * This function enables SPI host controller.
   *
   * Return:    0 always
   */
@@@ -475,7 -480,7 +480,7 @@@ static int cdns_prepare_transfer_hardwa
   * @ctlr:     Pointer to the spi_controller structure which provides
   *            information about the controller.
   *
 - * This function disables the SPI master controller when no slave selected.
 + * This function disables the SPI host controller when no target selected.
   * This function flush out if any pending data in FIFO.
   *
   * Return:    0 always
@@@ -486,15 -491,15 +491,15 @@@ static int cdns_unprepare_transfer_hard
        u32 ctrl_reg;
        unsigned int cnt = xspi->tx_fifo_depth;
  
 -      if (spi_controller_is_slave(ctlr)) {
 +      if (spi_controller_is_target(ctlr)) {
                while (cnt--)
                        cdns_spi_read(xspi, CDNS_SPI_RXD);
        }
  
 -      /* Disable the SPI if slave is deselected */
 +      /* Disable the SPI if target is deselected */
        ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR);
        ctrl_reg = (ctrl_reg & CDNS_SPI_CR_SSCTRL) >>  CDNS_SPI_SS_SHIFT;
 -      if (ctrl_reg == CDNS_SPI_NOSS || spi_controller_is_slave(ctlr))
 +      if (ctrl_reg == CDNS_SPI_NOSS || spi_controller_is_target(ctlr))
                cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE);
  
        /* Reset to default */
@@@ -521,14 -526,14 +526,14 @@@ static void cdns_spi_detect_fifo_depth(
  }
  
  /**
 - * cdns_slave_abort - Abort slave transfer
 + * cdns_target_abort - Abort target transfer
   * @ctlr:     Pointer to the spi_controller structure
   *
 - * This function abort slave transfer if there any transfer timeout.
 + * This function abort target transfer if there any transfer timeout.
   *
   * Return:      0 always
   */
 -static int cdns_slave_abort(struct spi_controller *ctlr)
 +static int cdns_target_abort(struct spi_controller *ctlr)
  {
        struct cdns_spi *xspi = spi_controller_get_devdata(ctlr);
        u32 intr_status;
@@@ -555,13 -560,13 +560,13 @@@ static int cdns_spi_probe(struct platfo
        struct spi_controller *ctlr;
        struct cdns_spi *xspi;
        u32 num_cs;
 -      bool slave;
 +      bool target;
  
 -      slave = of_property_read_bool(pdev->dev.of_node, "spi-slave");
 -      if (slave)
 -              ctlr = spi_alloc_slave(&pdev->dev, sizeof(*xspi));
 +      target = of_property_read_bool(pdev->dev.of_node, "spi-slave");
 +      if (target)
 +              ctlr = spi_alloc_target(&pdev->dev, sizeof(*xspi));
        else
 -              ctlr = spi_alloc_master(&pdev->dev, sizeof(*xspi));
 +              ctlr = spi_alloc_host(&pdev->dev, sizeof(*xspi));
  
        if (!ctlr)
                return -ENOMEM;
                goto remove_ctlr;
        }
  
 -      if (!spi_controller_is_slave(ctlr)) {
 +      if (!spi_controller_is_target(ctlr)) {
                xspi->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
                if (IS_ERR(xspi->ref_clk)) {
                        dev_err(&pdev->dev, "ref_clk clock not found.\n");
        cdns_spi_detect_fifo_depth(xspi);
  
        /* SPI controller initializations */
 -      cdns_spi_init_hw(xspi, spi_controller_is_slave(ctlr));
 +      cdns_spi_init_hw(xspi, spi_controller_is_target(ctlr));
  
        irq = platform_get_irq(pdev, 0);
 -      if (irq <= 0) {
 -              ret = -ENXIO;
 +      if (irq < 0) {
 +              ret = irq;
                goto clk_dis_all;
        }
  
        ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
        ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
  
 -      if (!spi_controller_is_slave(ctlr)) {
 +      if (!spi_controller_is_target(ctlr)) {
                ctlr->mode_bits |=  SPI_CS_HIGH;
                ctlr->set_cs = cdns_spi_chipselect;
                ctlr->auto_runtime_pm = true;
                pm_runtime_put_autosuspend(&pdev->dev);
        } else {
                ctlr->mode_bits |= SPI_NO_CS;
 -              ctlr->slave_abort = cdns_slave_abort;
 +              ctlr->target_abort = cdns_target_abort;
        }
        ret = spi_register_controller(ctlr);
        if (ret) {
        return ret;
  
  clk_dis_all:
 -      if (!spi_controller_is_slave(ctlr)) {
 +      if (!spi_controller_is_target(ctlr)) {
                pm_runtime_set_suspended(&pdev->dev);
                pm_runtime_disable(&pdev->dev);
                clk_disable_unprepare(xspi->ref_clk);
@@@ -690,6 -695,8 +695,6 @@@ remove_ctlr
   * This function is called if a device is physically removed from the system or
   * if the driver module is being unloaded. It frees all resources allocated to
   * the device.
 - *
 - * Return:    0 on success and error value on error
   */
  static void cdns_spi_remove(struct platform_device *pdev)
  {
@@@ -735,7 -742,7 +740,7 @@@ static int __maybe_unused cdns_spi_resu
        struct spi_controller *ctlr = dev_get_drvdata(dev);
        struct cdns_spi *xspi = spi_controller_get_devdata(ctlr);
  
 -      cdns_spi_init_hw(xspi, spi_controller_is_slave(ctlr));
 +      cdns_spi_init_hw(xspi, spi_controller_is_target(ctlr));
        return spi_controller_resume(ctlr);
  }
  
diff --combined drivers/spi/spi-stm32.c
index d16ee6e54de94f1169bfabf6c8de0bf1d6e8fb61,7ddf9db776b06570c9014b4b17088aa6679774dd..b6d66caba4c02eb3e988ad9a5f76317a4244af32
@@@ -238,7 -238,6 +238,7 @@@ struct stm32_spi
   * @baud_rate_div_min: minimum baud rate divisor
   * @baud_rate_div_max: maximum baud rate divisor
   * @has_fifo: boolean to know if fifo is used for driver
 + * @has_device_mode: is this compatible capable to switch on device mode
   * @flags: compatible specific SPI controller flags used at registration time
   */
  struct stm32_spi_cfg {
        unsigned int baud_rate_div_min;
        unsigned int baud_rate_div_max;
        bool has_fifo;
 +      bool has_device_mode;
        u16 flags;
  };
  
@@@ -1003,9 -1001,9 +1003,9 @@@ static int stm32_spi_prepare_msg(struc
        if (spi->cfg->set_number_of_data) {
                int ret;
  
-               ret = spi_split_transfers_maxsize(ctrl, msg,
-                                                 STM32H7_SPI_TSIZE_MAX,
-                                                 GFP_KERNEL | GFP_DMA);
+               ret = spi_split_transfers_maxwords(ctrl, msg,
+                                                  STM32H7_SPI_TSIZE_MAX,
+                                                  GFP_KERNEL | GFP_DMA);
                if (ret)
                        return ret;
        }
@@@ -1752,8 -1750,7 +1752,8 @@@ static const struct stm32_spi_cfg stm32
        .baud_rate_div_min = STM32F4_SPI_BR_DIV_MIN,
        .baud_rate_div_max = STM32F4_SPI_BR_DIV_MAX,
        .has_fifo = false,
 -      .flags = SPI_MASTER_MUST_TX,
 +      .has_device_mode = false,
 +      .flags = SPI_CONTROLLER_MUST_TX,
  };
  
  static const struct stm32_spi_cfg stm32h7_spi_cfg = {
        .baud_rate_div_min = STM32H7_SPI_MBR_DIV_MIN,
        .baud_rate_div_max = STM32H7_SPI_MBR_DIV_MAX,
        .has_fifo = true,
 +      .has_device_mode = true,
  };
  
  static const struct of_device_id stm32_spi_of_match[] = {
@@@ -1802,13 -1798,8 +1802,13 @@@ static int stm32_spi_probe(struct platf
        struct device_node *np = pdev->dev.of_node;
        bool device_mode;
        int ret;
 +      const struct stm32_spi_cfg *cfg = of_device_get_match_data(&pdev->dev);
  
        device_mode = of_property_read_bool(np, "spi-slave");
 +      if (!cfg->has_device_mode && device_mode) {
 +              dev_err(&pdev->dev, "spi-slave not supported\n");
 +              return -EPERM;
 +      }
  
        if (device_mode)
                ctrl = devm_spi_alloc_slave(&pdev->dev, sizeof(struct stm32_spi));
        spi->device_mode = device_mode;
        spin_lock_init(&spi->lock);
  
 -      spi->cfg = (const struct stm32_spi_cfg *)
 -              of_match_device(pdev->dev.driver->of_match_table,
 -                              &pdev->dev)->data;
 +      spi->cfg = cfg;
  
        spi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(spi->base))
  
        spi->irq = platform_get_irq(pdev, 0);
        if (spi->irq <= 0)
 -              return dev_err_probe(&pdev->dev, spi->irq,
 -                                   "failed to get irq\n");
 +              return spi->irq;
  
        ret = devm_request_threaded_irq(&pdev->dev, spi->irq,
                                        spi->cfg->irq_handler_event,
diff --combined drivers/spi/spi-sun6i.c
index 3f5b1556ece0227c4edfe2ab056d041408f4ff46,57c828e73c446372cbf05e9da1116bd02e4de764..fddc633097736a212df994130c514a2bd46f656e
@@@ -14,7 -14,7 +14,7 @@@
  #include <linux/interrupt.h>
  #include <linux/io.h>
  #include <linux/module.h>
 -#include <linux/of_device.h>
 +#include <linux/of.h>
  #include <linux/platform_device.h>
  #include <linux/pm_runtime.h>
  #include <linux/reset.h>
@@@ -83,9 -83,6 +83,9 @@@
  #define SUN6I_XMIT_CNT_REG            0x34
  
  #define SUN6I_BURST_CTL_CNT_REG               0x38
 +#define SUN6I_BURST_CTL_CNT_STC_MASK          GENMASK(23, 0)
 +#define SUN6I_BURST_CTL_CNT_DRM                       BIT(28)
 +#define SUN6I_BURST_CTL_CNT_QUAD_EN           BIT(29)
  
  #define SUN6I_TXDATA_REG              0x200
  #define SUN6I_RXDATA_REG              0x300
@@@ -93,7 -90,6 +93,7 @@@
  struct sun6i_spi_cfg {
        unsigned long           fifo_depth;
        bool                    has_clk_ctl;
 +      u32                     mode_bits;
  };
  
  struct sun6i_spi {
        struct reset_control    *rstc;
  
        struct completion       done;
+       struct completion       dma_rx_done;
  
        const u8                *tx_buf;
        u8                      *rx_buf;
@@@ -200,6 -197,13 +201,13 @@@ static size_t sun6i_spi_max_transfer_si
        return SUN6I_MAX_XFER_SIZE - 1;
  }
  
+ static void sun6i_spi_dma_rx_cb(void *param)
+ {
+       struct sun6i_spi *sspi = param;
+       complete(&sspi->dma_rx_done);
+ }
  static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi,
                                 struct spi_transfer *tfr)
  {
                struct dma_slave_config rxconf = {
                        .direction = DMA_DEV_TO_MEM,
                        .src_addr = sspi->dma_addr_rx,
-                       .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+                       .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
                        .src_maxburst = 8,
                };
  
                                                 DMA_PREP_INTERRUPT);
                if (!rxdesc)
                        return -EINVAL;
+               rxdesc->callback_param = sspi;
+               rxdesc->callback = sun6i_spi_dma_rx_cb;
        }
  
        txdesc = NULL;
@@@ -270,7 -276,7 +280,7 @@@ static int sun6i_spi_transfer_one(struc
        unsigned int div, div_cdr1, div_cdr2, timeout;
        unsigned int start, end, tx_time;
        unsigned int trig_level;
 -      unsigned int tx_len = 0, rx_len = 0;
 +      unsigned int tx_len = 0, rx_len = 0, nbits = 0;
        bool use_dma;
        int ret = 0;
        u32 reg;
                return -EINVAL;
  
        reinit_completion(&sspi->done);
+       reinit_completion(&sspi->dma_rx_done);
        sspi->tx_buf = tfr->tx_buf;
        sspi->rx_buf = tfr->rx_buf;
        sspi->len = tfr->len;
        sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, reg);
  
        /* Setup the transfer now... */
 -      if (sspi->tx_buf)
 +      if (sspi->tx_buf) {
                tx_len = tfr->len;
 +              nbits = tfr->tx_nbits;
 +      } else if (tfr->rx_buf) {
 +              nbits = tfr->rx_nbits;
 +      }
 +
 +      switch (nbits) {
 +      case SPI_NBITS_DUAL:
 +              reg = SUN6I_BURST_CTL_CNT_DRM;
 +              break;
 +      case SPI_NBITS_QUAD:
 +              reg = SUN6I_BURST_CTL_CNT_QUAD_EN;
 +              break;
 +      case SPI_NBITS_SINGLE:
 +      default:
 +              reg = FIELD_PREP(SUN6I_BURST_CTL_CNT_STC_MASK, tx_len);
 +      }
  
        /* Setup the counters */
 +      sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, reg);
        sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, tfr->len);
        sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len);
 -      sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len);
  
        if (!use_dma) {
                /* Fill the TX FIFO */
        start = jiffies;
        timeout = wait_for_completion_timeout(&sspi->done,
                                              msecs_to_jiffies(tx_time));
+       if (!use_dma) {
+               sun6i_spi_drain_fifo(sspi);
+       } else {
+               if (timeout && rx_len) {
+                       /*
+                        * Even though RX on the peripheral side has finished
+                        * RX DMA might still be in flight
+                        */
+                       timeout = wait_for_completion_timeout(&sspi->dma_rx_done,
+                                                             timeout);
+                       if (!timeout)
+                               dev_warn(&master->dev, "RX DMA timeout\n");
+               }
+       }
        end = jiffies;
        if (!timeout) {
                dev_warn(&master->dev,
@@@ -506,7 -513,6 +533,6 @@@ static irqreturn_t sun6i_spi_handler(in
        /* Transfer complete */
        if (status & SUN6I_INT_CTL_TC) {
                sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
-               sun6i_spi_drain_fifo(sspi);
                complete(&sspi->done);
                return IRQ_HANDLED;
        }
@@@ -643,8 -649,7 +669,8 @@@ static int sun6i_spi_probe(struct platf
        master->set_cs = sun6i_spi_set_cs;
        master->transfer_one = sun6i_spi_transfer_one;
        master->num_chipselect = 4;
 -      master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
 +      master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST |
 +                          sspi->cfg->mode_bits;
        master->bits_per_word_mask = SPI_BPW_MASK(8);
        master->dev.of_node = pdev->dev.of_node;
        master->auto_runtime_pm = true;
        }
  
        init_completion(&sspi->done);
+       init_completion(&sspi->dma_rx_done);
  
        sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
        if (IS_ERR(sspi->rstc)) {
@@@ -761,7 -767,6 +788,7 @@@ static const struct sun6i_spi_cfg sun8i
  
  static const struct sun6i_spi_cfg sun50i_r329_spi_cfg = {
        .fifo_depth     = SUN8I_FIFO_DEPTH,
 +      .mode_bits      = SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD,
  };
  
  static const struct of_device_id sun6i_spi_match[] = {
This page took 0.135091 seconds and 4 git commands to generate.