Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

spi: dw: Add generic DW SSI status-check method

The DW SSI errors handling method can be generically implemented for all
types of the transfers: IRQ, DMA and poll-based ones. It will be a
function which checks the overflow/underflow error flags and resets the
controller if any of them is set. In the framework of this commit we make
use of the new method to detect the errors in the IRQ- and DMA-based SPI
transfer execution procedures.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Link: https://lore.kernel.org/r/20201007235511.4935-17-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Serge Semin and committed by
Mark Brown
bf64b660 cf75baea

+37 -18
+34 -9
drivers/spi/spi-dw-core.c
··· 169 169 } 170 170 } 171 171 172 - static void int_error_stop(struct dw_spi *dws, const char *msg) 172 + int dw_spi_check_status(struct dw_spi *dws, bool raw) 173 173 { 174 - spi_reset_chip(dws); 174 + u32 irq_status; 175 + int ret = 0; 175 176 176 - dev_err(&dws->master->dev, "%s\n", msg); 177 - dws->master->cur_msg->status = -EIO; 178 - spi_finalize_current_transfer(dws->master); 177 + if (raw) 178 + irq_status = dw_readl(dws, DW_SPI_RISR); 179 + else 180 + irq_status = dw_readl(dws, DW_SPI_ISR); 181 + 182 + if (irq_status & SPI_INT_RXOI) { 183 + dev_err(&dws->master->dev, "RX FIFO overflow detected\n"); 184 + ret = -EIO; 185 + } 186 + 187 + if (irq_status & SPI_INT_RXUI) { 188 + dev_err(&dws->master->dev, "RX FIFO underflow detected\n"); 189 + ret = -EIO; 190 + } 191 + 192 + if (irq_status & SPI_INT_TXOI) { 193 + dev_err(&dws->master->dev, "TX FIFO overflow detected\n"); 194 + ret = -EIO; 195 + } 196 + 197 + /* Generically handle the erroneous situation */ 198 + if (ret) { 199 + spi_reset_chip(dws); 200 + if (dws->master->cur_msg) 201 + dws->master->cur_msg->status = ret; 202 + } 203 + 204 + return ret; 179 205 } 206 + EXPORT_SYMBOL_GPL(dw_spi_check_status); 180 207 181 208 static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) 182 209 { 183 210 u16 irq_status = dw_readl(dws, DW_SPI_ISR); 184 211 185 - /* Error handling */ 186 - if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { 187 - dw_readl(dws, DW_SPI_ICR); 188 - int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun"); 212 + if (dw_spi_check_status(dws, false)) { 213 + spi_finalize_current_transfer(dws->master); 189 214 return IRQ_HANDLED; 190 215 } 191 216
+2 -9
drivers/spi/spi-dw-dma.c
··· 176 176 177 177 static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws) 178 178 { 179 - u16 irq_status = dw_readl(dws, DW_SPI_ISR); 179 + dw_spi_check_status(dws, false); 180 180 181 - if (!irq_status) 182 - return IRQ_NONE; 183 - 184 - dw_readl(dws, DW_SPI_ICR); 185 - spi_reset_chip(dws); 186 - 187 - dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); 188 - dws->master->cur_msg->status = -EIO; 189 181 complete(&dws->dma_completion); 182 + 190 183 return IRQ_HANDLED; 191 184 } 192 185
+1
drivers/spi/spi-dw.h
··· 262 262 extern void dw_spi_set_cs(struct spi_device *spi, bool enable); 263 263 extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, 264 264 struct dw_spi_cfg *cfg); 265 + extern int dw_spi_check_status(struct dw_spi *dws, bool raw); 265 266 extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); 266 267 extern void dw_spi_remove_host(struct dw_spi *dws); 267 268 extern int dw_spi_suspend_host(struct dw_spi *dws);