aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-sun4i.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-sun4i.c')
-rw-r--r--drivers/spi/spi-sun4i.c75
1 files changed, 67 insertions, 8 deletions
diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c
index 4969dc10684a..c5cd635c28f3 100644
--- a/drivers/spi/spi-sun4i.c
+++ b/drivers/spi/spi-sun4i.c
@@ -46,6 +46,8 @@
46#define SUN4I_CTL_TP BIT(18) 46#define SUN4I_CTL_TP BIT(18)
47 47
48#define SUN4I_INT_CTL_REG 0x0c 48#define SUN4I_INT_CTL_REG 0x0c
49#define SUN4I_INT_CTL_RF_F34 BIT(4)
50#define SUN4I_INT_CTL_TF_E34 BIT(12)
49#define SUN4I_INT_CTL_TC BIT(16) 51#define SUN4I_INT_CTL_TC BIT(16)
50 52
51#define SUN4I_INT_STA_REG 0x10 53#define SUN4I_INT_STA_REG 0x10
@@ -61,11 +63,14 @@
61#define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8) 63#define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8)
62#define SUN4I_CLK_CTL_DRS BIT(12) 64#define SUN4I_CLK_CTL_DRS BIT(12)
63 65
66#define SUN4I_MAX_XFER_SIZE 0xffffff
67
64#define SUN4I_BURST_CNT_REG 0x20 68#define SUN4I_BURST_CNT_REG 0x20
65#define SUN4I_BURST_CNT(cnt) ((cnt) & 0xffffff) 69#define SUN4I_BURST_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE)
66 70
67#define SUN4I_XMIT_CNT_REG 0x24 71#define SUN4I_XMIT_CNT_REG 0x24
68#define SUN4I_XMIT_CNT(cnt) ((cnt) & 0xffffff) 72#define SUN4I_XMIT_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE)
73
69 74
70#define SUN4I_FIFO_STA_REG 0x28 75#define SUN4I_FIFO_STA_REG 0x28
71#define SUN4I_FIFO_STA_RF_CNT_MASK 0x7f 76#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)
96 writel(value, sspi->base_addr + reg); 101 writel(value, sspi->base_addr + reg);
97} 102}
98 103
104static inline u32 sun4i_spi_get_tx_fifo_count(struct sun4i_spi *sspi)
105{
106 u32 reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG);
107
108 reg >>= SUN4I_FIFO_STA_TF_CNT_BITS;
109
110 return reg & SUN4I_FIFO_STA_TF_CNT_MASK;
111}
112
113static inline void sun4i_spi_enable_interrupt(struct sun4i_spi *sspi, u32 mask)
114{
115 u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG);
116
117 reg |= mask;
118 sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg);
119}
120
121static inline void sun4i_spi_disable_interrupt(struct sun4i_spi *sspi, u32 mask)
122{
123 u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG);
124
125 reg &= ~mask;
126 sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg);
127}
128
99static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len) 129static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len)
100{ 130{
101 u32 reg, cnt; 131 u32 reg, cnt;
@@ -118,10 +148,13 @@ static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len)
118 148
119static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len) 149static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len)
120{ 150{
151 u32 cnt;
121 u8 byte; 152 u8 byte;
122 153
123 if (len > sspi->len) 154 /* See how much data we can fit */
124 len = sspi->len; 155 cnt = SUN4I_FIFO_DEPTH - sun4i_spi_get_tx_fifo_count(sspi);
156
157 len = min3(len, (int)cnt, sspi->len);
125 158
126 while (len--) { 159 while (len--) {
127 byte = sspi->tx_buf ? *sspi->tx_buf++ : 0; 160 byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
@@ -184,10 +217,10 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
184 u32 reg; 217 u32 reg;
185 218
186 /* We don't support transfer larger than the FIFO */ 219 /* We don't support transfer larger than the FIFO */
187 if (tfr->len > SUN4I_FIFO_DEPTH) 220 if (tfr->len > SUN4I_MAX_XFER_SIZE)
188 return -EMSGSIZE; 221 return -EMSGSIZE;
189 222
190 if (tfr->tx_buf && tfr->len >= SUN4I_FIFO_DEPTH) 223 if (tfr->tx_buf && tfr->len >= SUN4I_MAX_XFER_SIZE)
191 return -EMSGSIZE; 224 return -EMSGSIZE;
192 225
193 reinit_completion(&sspi->done); 226 reinit_completion(&sspi->done);
@@ -286,7 +319,11 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
286 sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1); 319 sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1);
287 320
288 /* Enable the interrupts */ 321 /* Enable the interrupts */
289 sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC); 322 sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC |
323 SUN4I_INT_CTL_RF_F34);
324 /* Only enable Tx FIFO interrupt if we really need it */
325 if (tx_len > SUN4I_FIFO_DEPTH)
326 sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TF_E34);
290 327
291 /* Start the transfer */ 328 /* Start the transfer */
292 reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); 329 reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
@@ -306,7 +343,6 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
306 goto out; 343 goto out;
307 } 344 }
308 345
309 sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH);
310 346
311out: 347out:
312 sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0); 348 sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0);
@@ -322,10 +358,33 @@ static irqreturn_t sun4i_spi_handler(int irq, void *dev_id)
322 /* Transfer complete */ 358 /* Transfer complete */
323 if (status & SUN4I_INT_CTL_TC) { 359 if (status & SUN4I_INT_CTL_TC) {
324 sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC); 360 sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC);
361 sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH);
325 complete(&sspi->done); 362 complete(&sspi->done);
326 return IRQ_HANDLED; 363 return IRQ_HANDLED;
327 } 364 }
328 365
366 /* Receive FIFO 3/4 full */
367 if (status & SUN4I_INT_CTL_RF_F34) {
368 sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH);
369 /* Only clear the interrupt _after_ draining the FIFO */
370 sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_RF_F34);
371 return IRQ_HANDLED;
372 }
373
374 /* Transmit FIFO 3/4 empty */
375 if (status & SUN4I_INT_CTL_TF_E34) {
376 sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH);
377
378 if (!sspi->len)
379 /* nothing left to transmit */
380 sun4i_spi_disable_interrupt(sspi, SUN4I_INT_CTL_TF_E34);
381
382 /* Only clear the interrupt _after_ re-seeding the FIFO */
383 sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TF_E34);
384
385 return IRQ_HANDLED;
386 }
387
329 return IRQ_NONE; 388 return IRQ_NONE;
330} 389}
331 390