Commit 411b0f30 authored by Mark Brown's avatar Mark Brown

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