Unverified Commit 411b0f30 authored by Mark Brown's avatar Mark Brown
Browse files

Merge series "spi: spi-sun6i: One fix and some improvements" from Marc...

Merge series "spi: spi-sun6i: One fix and some improvements" from Marc Kleine-Budde <mkl@pengutronix.de>:

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
parents 2d9a7446 7716fa80
Loading
Loading
Loading
Loading
+22 −17
Original line number Diff line number Diff line
@@ -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,
	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 = {
+8 −7
Original line number Diff line number Diff line
@@ -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);
+5 −0
Original line number Diff line number Diff line
@@ -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 },
+26 −20
Original line number Diff line number Diff line
@@ -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;
}
+41 −50
Original line number Diff line number Diff line
@@ -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>
@@ -58,10 +59,8 @@
#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
@@ -73,13 +72,10 @@
#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 */
Loading