Merge series "spi: spi-sun6i: One fix and some improvements" from Marc Kleine-Budde...
authorMark Brown <broonie@kernel.org>
Tue, 7 Jul 2020 13:26:33 +0000 (14:26 +0100)
committerMark Brown <broonie@kernel.org>
Tue, 7 Jul 2020 13:26:33 +0000 (14:26 +0100)
Hello,

this series first fixes the calculation of the clock rate. The driver will
round up to the nearest clock rate instead of rounding down. Resulting in SPI
devices accessed with a too high SPI clock.

The remaining patches improve the performance of the driver. The changes range
from micro-optimizations like reducing MMIO writes to the controller to
reducing the number of needed interrupts in some use cases.

regards,
Marc

changes since v1:
- added Maxime Ripard's to the existing patches
- 06/10: (was 05/10 in v1)
  "spi: spi-sun6i: sun6i_spi_drain_fifo(): introduce sun6i_spi_get_rx_fifo_count() and make use of it"
  use FIELD_GET instead of open coding it
  (tnx: Maxime Ripard)
- 05/10: "spi: spi-sun6i: sun6i_spi_get_tx_fifo_count: Convert manual shift+mask to FIELD_GET()"
  new patch

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

drivers/spi/spi-fsl-dspi.c
drivers/spi/spi-mt65xx.c
drivers/spi/spi-pxa2xx.c
drivers/spi/spi-stm32-qspi.c
drivers/spi/spi-sun6i.c
drivers/spi/spidev.c

index 58190c9..91c6aff 100644 (file)
@@ -1109,6 +1109,8 @@ static int dspi_suspend(struct device *dev)
        struct spi_controller *ctlr = dev_get_drvdata(dev);
        struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr);
 
+       if (dspi->irq)
+               disable_irq(dspi->irq);
        spi_controller_suspend(ctlr);
        clk_disable_unprepare(dspi->clk);
 
@@ -1129,6 +1131,8 @@ static int dspi_resume(struct device *dev)
        if (ret)
                return ret;
        spi_controller_resume(ctlr);
+       if (dspi->irq)
+               enable_irq(dspi->irq);
 
        return 0;
 }
@@ -1385,22 +1389,22 @@ static int dspi_probe(struct platform_device *pdev)
                goto poll_mode;
        }
 
-       ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt,
-                              IRQF_SHARED, pdev->name, dspi);
+       init_completion(&dspi->xfer_done);
+
+       ret = request_threaded_irq(dspi->irq, dspi_interrupt, NULL,
+                                  IRQF_SHARED, pdev->name, dspi);
        if (ret < 0) {
                dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n");
                goto out_clk_put;
        }
 
-       init_completion(&dspi->xfer_done);
-
 poll_mode:
 
        if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) {
                ret = dspi_request_dma(dspi, res->start);
                if (ret < 0) {
                        dev_err(&pdev->dev, "can't get dma channels\n");
-                       goto out_clk_put;
+                       goto out_free_irq;
                }
        }
 
@@ -1415,11 +1419,14 @@ poll_mode:
        ret = spi_register_controller(ctlr);
        if (ret != 0) {
                dev_err(&pdev->dev, "Problem registering DSPI ctlr\n");
-               goto out_clk_put;
+               goto out_free_irq;
        }
 
        return ret;
 
+out_free_irq:
+       if (dspi->irq)
+               free_irq(dspi->irq, dspi);
 out_clk_put:
        clk_disable_unprepare(dspi->clk);
 out_ctlr_put:
@@ -1434,18 +1441,8 @@ static int dspi_remove(struct platform_device *pdev)
        struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr);
 
        /* Disconnect from the SPI framework */
-       dspi_release_dma(dspi);
-       clk_disable_unprepare(dspi->clk);
        spi_unregister_controller(dspi->ctlr);
 
-       return 0;
-}
-
-static void dspi_shutdown(struct platform_device *pdev)
-{
-       struct spi_controller *ctlr = platform_get_drvdata(pdev);
-       struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr);
-
        /* Disable RX and TX */
        regmap_update_bits(dspi->regmap, SPI_MCR,
                           SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF,
@@ -1455,8 +1452,16 @@ static void dspi_shutdown(struct platform_device *pdev)
        regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_HALT, SPI_MCR_HALT);
 
        dspi_release_dma(dspi);
+       if (dspi->irq)
+               free_irq(dspi->irq, dspi);
        clk_disable_unprepare(dspi->clk);
-       spi_unregister_controller(dspi->ctlr);
+
+       return 0;
+}
+
+static void dspi_shutdown(struct platform_device *pdev)
+{
+       dspi_remove(pdev);
 }
 
 static struct platform_driver fsl_dspi_driver = {
index 6783e12..a556795 100644 (file)
@@ -36,7 +36,6 @@
 #define SPI_CFG0_SCK_LOW_OFFSET           8
 #define SPI_CFG0_CS_HOLD_OFFSET           16
 #define SPI_CFG0_CS_SETUP_OFFSET          24
-#define SPI_ADJUST_CFG0_SCK_LOW_OFFSET    16
 #define SPI_ADJUST_CFG0_CS_HOLD_OFFSET    0
 #define SPI_ADJUST_CFG0_CS_SETUP_OFFSET   16
 
@@ -48,6 +47,8 @@
 #define SPI_CFG1_CS_IDLE_MASK             0xff
 #define SPI_CFG1_PACKET_LOOP_MASK         0xff00
 #define SPI_CFG1_PACKET_LENGTH_MASK       0x3ff0000
+#define SPI_CFG2_SCK_HIGH_OFFSET          0
+#define SPI_CFG2_SCK_LOW_OFFSET           16
 
 #define SPI_CMD_ACT                  BIT(0)
 #define SPI_CMD_RESUME               BIT(1)
@@ -283,7 +284,7 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
 static void mtk_spi_prepare_transfer(struct spi_master *master,
                                     struct spi_transfer *xfer)
 {
-       u32 spi_clk_hz, div, sck_time, cs_time, reg_val = 0;
+       u32 spi_clk_hz, div, sck_time, cs_time, reg_val;
        struct mtk_spi *mdata = spi_master_get_devdata(master);
 
        spi_clk_hz = clk_get_rate(mdata->spi_clk);
@@ -296,18 +297,18 @@ static void mtk_spi_prepare_transfer(struct spi_master *master,
        cs_time = sck_time * 2;
 
        if (mdata->dev_comp->enhance_timing) {
+               reg_val = (((sck_time - 1) & 0xffff)
+                          << SPI_CFG2_SCK_HIGH_OFFSET);
                reg_val |= (((sck_time - 1) & 0xffff)
-                          << SPI_CFG0_SCK_HIGH_OFFSET);
-               reg_val |= (((sck_time - 1) & 0xffff)
-                          << SPI_ADJUST_CFG0_SCK_LOW_OFFSET);
+                          << SPI_CFG2_SCK_LOW_OFFSET);
                writel(reg_val, mdata->base + SPI_CFG2_REG);
-               reg_val |= (((cs_time - 1) & 0xffff)
+               reg_val = (((cs_time - 1) & 0xffff)
                           << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
                reg_val |= (((cs_time - 1) & 0xffff)
                           << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
                writel(reg_val, mdata->base + SPI_CFG0_REG);
        } else {
-               reg_val |= (((sck_time - 1) & 0xff)
+               reg_val = (((sck_time - 1) & 0xff)
                           << SPI_CFG0_SCK_HIGH_OFFSET);
                reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_LOW_OFFSET);
                reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET);
index 6721910..0040362 100644 (file)
@@ -1485,6 +1485,11 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {
        { PCI_VDEVICE(INTEL, 0x4daa), LPSS_CNL_SSP },
        { PCI_VDEVICE(INTEL, 0x4dab), LPSS_CNL_SSP },
        { PCI_VDEVICE(INTEL, 0x4dfb), LPSS_CNL_SSP },
+       /* TGL-H */
+       { PCI_VDEVICE(INTEL, 0x43aa), LPSS_CNL_SSP },
+       { PCI_VDEVICE(INTEL, 0x43ab), LPSS_CNL_SSP },
+       { PCI_VDEVICE(INTEL, 0x43fb), LPSS_CNL_SSP },
+       { PCI_VDEVICE(INTEL, 0x43fd), LPSS_CNL_SSP },
        /* APL */
        { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
        { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
index 3c44bb2..a900962 100644 (file)
@@ -553,20 +553,6 @@ static const struct spi_controller_mem_ops stm32_qspi_mem_ops = {
        .exec_op = stm32_qspi_exec_op,
 };
 
-static void stm32_qspi_release(struct stm32_qspi *qspi)
-{
-       pm_runtime_get_sync(qspi->dev);
-       /* disable qspi */
-       writel_relaxed(0, qspi->io_base + QSPI_CR);
-       stm32_qspi_dma_free(qspi);
-       mutex_destroy(&qspi->lock);
-       pm_runtime_put_noidle(qspi->dev);
-       pm_runtime_disable(qspi->dev);
-       pm_runtime_set_suspended(qspi->dev);
-       pm_runtime_dont_use_autosuspend(qspi->dev);
-       clk_disable_unprepare(qspi->clk);
-}
-
 static int stm32_qspi_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -642,7 +628,7 @@ static int stm32_qspi_probe(struct platform_device *pdev)
        if (IS_ERR(rstc)) {
                ret = PTR_ERR(rstc);
                if (ret == -EPROBE_DEFER)
-                       goto err_qspi_release;
+                       goto err_clk_disable;
        } else {
                reset_control_assert(rstc);
                udelay(2);
@@ -653,7 +639,7 @@ static int stm32_qspi_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, qspi);
        ret = stm32_qspi_dma_setup(qspi);
        if (ret)
-               goto err_qspi_release;
+               goto err_dma_free;
 
        mutex_init(&qspi->lock);
 
@@ -673,15 +659,26 @@ static int stm32_qspi_probe(struct platform_device *pdev)
 
        ret = devm_spi_register_master(dev, ctrl);
        if (ret)
-               goto err_qspi_release;
+               goto err_pm_runtime_free;
 
        pm_runtime_mark_last_busy(dev);
        pm_runtime_put_autosuspend(dev);
 
        return 0;
 
-err_qspi_release:
-       stm32_qspi_release(qspi);
+err_pm_runtime_free:
+       pm_runtime_get_sync(qspi->dev);
+       /* disable qspi */
+       writel_relaxed(0, qspi->io_base + QSPI_CR);
+       mutex_destroy(&qspi->lock);
+       pm_runtime_put_noidle(qspi->dev);
+       pm_runtime_disable(qspi->dev);
+       pm_runtime_set_suspended(qspi->dev);
+       pm_runtime_dont_use_autosuspend(qspi->dev);
+err_dma_free:
+       stm32_qspi_dma_free(qspi);
+err_clk_disable:
+       clk_disable_unprepare(qspi->clk);
 err_master_put:
        spi_master_put(qspi->ctrl);
 
@@ -692,7 +689,16 @@ static int stm32_qspi_remove(struct platform_device *pdev)
 {
        struct stm32_qspi *qspi = platform_get_drvdata(pdev);
 
-       stm32_qspi_release(qspi);
+       pm_runtime_get_sync(qspi->dev);
+       /* disable qspi */
+       writel_relaxed(0, qspi->io_base + QSPI_CR);
+       stm32_qspi_dma_free(qspi);
+       mutex_destroy(&qspi->lock);
+       pm_runtime_put_noidle(qspi->dev);
+       pm_runtime_disable(qspi->dev);
+       pm_runtime_set_suspended(qspi->dev);
+       pm_runtime_dont_use_autosuspend(qspi->dev);
+       clk_disable_unprepare(qspi->clk);
 
        return 0;
 }
index ecea155..19238e1 100644 (file)
@@ -7,6 +7,7 @@
  * Maxime Ripard <maxime.ripard@free-electrons.com>
  */
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #define SUN6I_FIFO_CTL_TF_RST                  BIT(31)
 
 #define SUN6I_FIFO_STA_REG             0x1c
-#define SUN6I_FIFO_STA_RF_CNT_MASK             0x7f
-#define SUN6I_FIFO_STA_RF_CNT_BITS             0
-#define SUN6I_FIFO_STA_TF_CNT_MASK             0x7f
-#define SUN6I_FIFO_STA_TF_CNT_BITS             16
+#define SUN6I_FIFO_STA_RF_CNT_MASK             GENMASK(7, 0)
+#define SUN6I_FIFO_STA_TF_CNT_MASK             GENMASK(23, 16)
 
 #define SUN6I_CLK_CTL_REG              0x24
 #define SUN6I_CLK_CTL_CDR2_MASK                        0xff
 #define SUN6I_MAX_XFER_SIZE            0xffffff
 
 #define SUN6I_BURST_CNT_REG            0x30
-#define SUN6I_BURST_CNT(cnt)                   ((cnt) & SUN6I_MAX_XFER_SIZE)
 
 #define SUN6I_XMIT_CNT_REG             0x34
-#define SUN6I_XMIT_CNT(cnt)                    ((cnt) & SUN6I_MAX_XFER_SIZE)
 
 #define SUN6I_BURST_CTL_CNT_REG                0x38
-#define SUN6I_BURST_CTL_CNT_STC(cnt)           ((cnt) & SUN6I_MAX_XFER_SIZE)
 
 #define SUN6I_TXDATA_REG               0x200
 #define SUN6I_RXDATA_REG               0x300
@@ -109,21 +105,18 @@ static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value)
        writel(value, sspi->base_addr + reg);
 }
 
-static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi)
+static inline u32 sun6i_spi_get_rx_fifo_count(struct sun6i_spi *sspi)
 {
        u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
 
-       reg >>= SUN6I_FIFO_STA_TF_CNT_BITS;
-
-       return reg & SUN6I_FIFO_STA_TF_CNT_MASK;
+       return FIELD_GET(SUN6I_FIFO_STA_RF_CNT_MASK, reg);
 }
 
-static inline void sun6i_spi_enable_interrupt(struct sun6i_spi *sspi, u32 mask)
+static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi)
 {
-       u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG);
+       u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
 
-       reg |= mask;
-       sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
+       return FIELD_GET(SUN6I_FIFO_STA_TF_CNT_MASK, reg);
 }
 
 static inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask)
@@ -134,18 +127,13 @@ static inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask)
        sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
 }
 
-static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
+static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi)
 {
-       u32 reg, cnt;
+       u32 len;
        u8 byte;
 
        /* See how much data is available */
-       reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
-       reg &= SUN6I_FIFO_STA_RF_CNT_MASK;
-       cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS;
-
-       if (len > cnt)
-               len = cnt;
+       len = sun6i_spi_get_rx_fifo_count(sspi);
 
        while (len--) {
                byte = readb(sspi->base_addr + SUN6I_RXDATA_REG);
@@ -154,15 +142,16 @@ static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
        }
 }
 
-static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len)
+static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi)
 {
        u32 cnt;
+       int len;
        u8 byte;
 
        /* See how much data we can fit */
        cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi);
 
-       len = min3(len, (int)cnt, sspi->len);
+       len = min((int)cnt, sspi->len);
 
        while (len--) {
                byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
@@ -198,10 +187,10 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
                                  struct spi_transfer *tfr)
 {
        struct sun6i_spi *sspi = spi_master_get_devdata(master);
-       unsigned int mclk_rate, div, timeout;
+       unsigned int mclk_rate, div, div_cdr1, div_cdr2, timeout;
        unsigned int start, end, tx_time;
        unsigned int trig_level;
-       unsigned int tx_len = 0;
+       unsigned int tx_len = 0, rx_len = 0;
        int ret = 0;
        u32 reg;
 
@@ -256,10 +245,12 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
         * If it's a TX only transfer, we don't want to fill the RX
         * FIFO with bogus data
         */
-       if (sspi->rx_buf)
+       if (sspi->rx_buf) {
                reg &= ~SUN6I_TFR_CTL_DHB;
-       else
+               rx_len = tfr->len;
+       } else {
                reg |= SUN6I_TFR_CTL_DHB;
+       }
 
        /* We want to control the chip select manually */
        reg |= SUN6I_TFR_CTL_CS_MANUAL;
@@ -287,15 +278,15 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
         * First try CDR2, and if we can't reach the expected
         * frequency, fall back to CDR1.
         */
-       div = mclk_rate / (2 * tfr->speed_hz);
-       if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
-               if (div > 0)
-                       div--;
-
-               reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS;
+       div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
+       div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
+       if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
+               reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
+               tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
        } else {
-               div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
+               div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
                reg = SUN6I_CLK_CTL_CDR1(div);
+               tfr->effective_speed_hz = mclk_rate / (1 << div);
        }
 
        sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
@@ -305,20 +296,22 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
                tx_len = tfr->len;
 
        /* Setup the counters */
-       sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, SUN6I_BURST_CNT(tfr->len));
-       sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, SUN6I_XMIT_CNT(tx_len));
-       sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG,
-                       SUN6I_BURST_CTL_CNT_STC(tx_len));
+       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);
 
        /* Fill the TX FIFO */
-       sun6i_spi_fill_fifo(sspi, sspi->fifo_depth);
+       sun6i_spi_fill_fifo(sspi);
 
        /* Enable the interrupts */
-       sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC);
-       sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC |
-                                        SUN6I_INT_CTL_RF_RDY);
+       reg = SUN6I_INT_CTL_TC;
+
+       if (rx_len > sspi->fifo_depth)
+               reg |= SUN6I_INT_CTL_RF_RDY;
        if (tx_len > sspi->fifo_depth)
-               sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ);
+               reg |= SUN6I_INT_CTL_TF_ERQ;
+
+       sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
 
        /* Start the transfer */
        reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
@@ -335,10 +328,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
                         dev_name(&spi->dev), tfr->len, tfr->speed_hz,
                         jiffies_to_msecs(end - start), tx_time);
                ret = -ETIMEDOUT;
-               goto out;
        }
 
-out:
        sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
 
        return ret;
@@ -352,14 +343,14 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
        /* 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, sspi->fifo_depth);
+               sun6i_spi_drain_fifo(sspi);
                complete(&sspi->done);
                return IRQ_HANDLED;
        }
 
        /* Receive FIFO 3/4 full */
        if (status & SUN6I_INT_CTL_RF_RDY) {
-               sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH);
+               sun6i_spi_drain_fifo(sspi);
                /* Only clear the interrupt _after_ draining the FIFO */
                sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_RF_RDY);
                return IRQ_HANDLED;
@@ -367,7 +358,7 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
 
        /* Transmit FIFO 3/4 empty */
        if (status & SUN6I_INT_CTL_TF_ERQ) {
-               sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH);
+               sun6i_spi_fill_fifo(sspi);
 
                if (!sspi->len)
                        /* nothing left to transmit */
index d753df7..59e0767 100644 (file)
@@ -609,15 +609,20 @@ err_find_dev:
 static int spidev_release(struct inode *inode, struct file *filp)
 {
        struct spidev_data      *spidev;
+       int                     dofree;
 
        mutex_lock(&device_list_lock);
        spidev = filp->private_data;
        filp->private_data = NULL;
 
+       spin_lock_irq(&spidev->spi_lock);
+       /* ... after we unbound from the underlying device? */
+       dofree = (spidev->spi == NULL);
+       spin_unlock_irq(&spidev->spi_lock);
+
        /* last close? */
        spidev->users--;
        if (!spidev->users) {
-               int             dofree;
 
                kfree(spidev->tx_buffer);
                spidev->tx_buffer = NULL;
@@ -625,19 +630,14 @@ static int spidev_release(struct inode *inode, struct file *filp)
                kfree(spidev->rx_buffer);
                spidev->rx_buffer = NULL;
 
-               spin_lock_irq(&spidev->spi_lock);
-               if (spidev->spi)
-                       spidev->speed_hz = spidev->spi->max_speed_hz;
-
-               /* ... after we unbound from the underlying device? */
-               dofree = (spidev->spi == NULL);
-               spin_unlock_irq(&spidev->spi_lock);
-
                if (dofree)
                        kfree(spidev);
+               else
+                       spidev->speed_hz = spidev->spi->max_speed_hz;
        }
 #ifdef CONFIG_SPI_SLAVE
-       spi_slave_abort(spidev->spi);
+       if (!dofree)
+               spi_slave_abort(spidev->spi);
 #endif
        mutex_unlock(&device_list_lock);
 
@@ -787,13 +787,13 @@ static int spidev_remove(struct spi_device *spi)
 {
        struct spidev_data      *spidev = spi_get_drvdata(spi);
 
+       /* prevent new opens */
+       mutex_lock(&device_list_lock);
        /* make sure ops on existing fds can abort cleanly */
        spin_lock_irq(&spidev->spi_lock);
        spidev->spi = NULL;
        spin_unlock_irq(&spidev->spi_lock);
 
-       /* prevent new opens */
-       mutex_lock(&device_list_lock);
        list_del(&spidev->device_entry);
        device_destroy(spidev_class, spidev->devt);
        clear_bit(MINOR(spidev->devt), minors);