From fdd69714af50ac0197c98b10b3cf8c44599e053d Mon Sep 17 00:00:00 2001 From: zador-blood-stained Date: Fri, 4 Nov 2016 22:05:14 +0300 Subject: [PATCH] Add large SPI transfers patch to sunxi-next --- .../spi-sunxi-allow-large-transfers.patch | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 patch/kernel/sunxi-next/spi-sunxi-allow-large-transfers.patch diff --git a/patch/kernel/sunxi-next/spi-sunxi-allow-large-transfers.patch b/patch/kernel/sunxi-next/spi-sunxi-allow-large-transfers.patch new file mode 100644 index 000000000..c82100bb8 --- /dev/null +++ b/patch/kernel/sunxi-next/spi-sunxi-allow-large-transfers.patch @@ -0,0 +1,158 @@ +diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c +index 4969dc1..138f224 100644 +--- a/drivers/spi/spi-sun4i.c ++++ b/drivers/spi/spi-sun4i.c +@@ -46,6 +46,8 @@ + #define SUN4I_CTL_TP BIT(18) + + #define SUN4I_INT_CTL_REG 0x0c ++#define SUN4I_INT_CTL_RF_F34 BIT(4) ++#define SUN4I_INT_CTL_TF_E34 BIT(12) + #define SUN4I_INT_CTL_TC BIT(16) + + #define SUN4I_INT_STA_REG 0x10 +@@ -61,11 +63,14 @@ + #define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8) + #define SUN4I_CLK_CTL_DRS BIT(12) + ++#define SUN4I_MAX_XFER_SIZE 0xffffff ++ + #define SUN4I_BURST_CNT_REG 0x20 +-#define SUN4I_BURST_CNT(cnt) ((cnt) & 0xffffff) ++#define SUN4I_BURST_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE) + + #define SUN4I_XMIT_CNT_REG 0x24 +-#define SUN4I_XMIT_CNT(cnt) ((cnt) & 0xffffff) ++#define SUN4I_XMIT_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE) ++ + + #define SUN4I_FIFO_STA_REG 0x28 + #define SUN4I_FIFO_STA_RF_CNT_MASK 0x7f +@@ -96,6 +101,31 @@ static inline void sun4i_spi_write(struct sun4i_spi *sspi, u32 reg, u32 value) + writel(value, sspi->base_addr + reg); + } + ++static inline u32 sun4i_spi_get_tx_fifo_count(struct sun4i_spi *sspi) ++{ ++ u32 reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG); ++ ++ reg >>= SUN4I_FIFO_STA_TF_CNT_BITS; ++ ++ return reg & SUN4I_FIFO_STA_TF_CNT_MASK; ++} ++ ++static inline void sun4i_spi_enable_interrupt(struct sun4i_spi *sspi, u32 mask) ++{ ++ u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG); ++ ++ reg |= mask; ++ sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg); ++} ++ ++static inline void sun4i_spi_disable_interrupt(struct sun4i_spi *sspi, u32 mask) ++{ ++ u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG); ++ ++ reg &= ~mask; ++ sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg); ++} ++ + static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len) + { + u32 reg, cnt; +@@ -118,10 +148,13 @@ static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len) + + static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len) + { ++ u32 cnt; + u8 byte; + +- if (len > sspi->len) +- len = sspi->len; ++ /* See how much data we can fit */ ++ cnt = SUN4I_FIFO_DEPTH - sun4i_spi_get_tx_fifo_count(sspi); ++ ++ len = min3(len, (int)cnt, sspi->len); + + while (len--) { + byte = sspi->tx_buf ? *sspi->tx_buf++ : 0; +@@ -169,7 +202,7 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) + + static size_t sun4i_spi_max_transfer_size(struct spi_device *spi) + { +- return SUN4I_FIFO_DEPTH - 1; ++ return SUN4I_MAX_XFER_SIZE - 1; + } + + static int sun4i_spi_transfer_one(struct spi_master *master, +@@ -183,11 +216,10 @@ static int sun4i_spi_transfer_one(struct spi_master *master, + int ret = 0; + u32 reg; + +- /* We don't support transfer larger than the FIFO */ +- if (tfr->len > SUN4I_FIFO_DEPTH) ++ if (tfr->len > SUN4I_MAX_XFER_SIZE) + return -EMSGSIZE; + +- if (tfr->tx_buf && tfr->len >= SUN4I_FIFO_DEPTH) ++ if (tfr->tx_buf && tfr->len >= SUN4I_MAX_XFER_SIZE) + return -EMSGSIZE; + + reinit_completion(&sspi->done); +@@ -286,7 +318,11 @@ static int sun4i_spi_transfer_one(struct spi_master *master, + sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1); + + /* Enable the interrupts */ +- sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC); ++ sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC | ++ SUN4I_INT_CTL_RF_F34); ++ /* Only enable Tx FIFO interrupt if we really need it */ ++ if (tx_len > SUN4I_FIFO_DEPTH) ++ sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TF_E34); + + /* Start the transfer */ + reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); +@@ -306,8 +342,6 @@ static int sun4i_spi_transfer_one(struct spi_master *master, + goto out; + } + +- sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); +- + out: + sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0); + +@@ -322,10 +356,33 @@ static irqreturn_t sun4i_spi_handler(int irq, void *dev_id) + /* Transfer complete */ + if (status & SUN4I_INT_CTL_TC) { + sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC); ++ sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); + complete(&sspi->done); + return IRQ_HANDLED; + } + ++ /* Receive FIFO 3/4 full */ ++ if (status & SUN4I_INT_CTL_RF_F34) { ++ sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); ++ /* Only clear the interrupt _after_ draining the FIFO */ ++ sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_RF_F34); ++ return IRQ_HANDLED; ++ } ++ ++ /* Transmit FIFO 3/4 empty */ ++ if (status & SUN4I_INT_CTL_TF_E34) { ++ sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); ++ ++ if (!sspi->len) ++ /* nothing left to transmit */ ++ sun4i_spi_disable_interrupt(sspi, SUN4I_INT_CTL_TF_E34); ++ ++ /* Only clear the interrupt _after_ re-seeding the FIFO */ ++ sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TF_E34); ++ ++ return IRQ_HANDLED; ++ } ++ + return IRQ_NONE; + } + +