From 3e247b0fb540a9a503a71ef6b918a3443e49655f Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 7 Jun 2018 19:40:32 +0000 Subject: [PATCH 01/40] spi: remove unused adi_spi3.h header include/linux/spi/adi_spi3.h is unused since commit 47838669de9d ("spi: remove blackfin related host drivers") Finish the cleaning by removing it. Signed-off-by: Corentin Labbe Signed-off-by: Mark Brown --- include/linux/spi/adi_spi3.h | 254 ----------------------------------- 1 file changed, 254 deletions(-) delete mode 100644 include/linux/spi/adi_spi3.h diff --git a/include/linux/spi/adi_spi3.h b/include/linux/spi/adi_spi3.h deleted file mode 100644 index c84123aa1d06..000000000000 --- a/include/linux/spi/adi_spi3.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Analog Devices SPI3 controller driver - * - * Copyright (c) 2014 Analog Devices Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _ADI_SPI3_H_ -#define _ADI_SPI3_H_ - -#include - -/* SPI_CONTROL */ -#define SPI_CTL_EN 0x00000001 /* Enable */ -#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */ -#define SPI_CTL_PSSE 0x00000004 /* controls modf error in master mode */ -#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */ -#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */ -#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */ -#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */ -#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in-between transfers */ -#define SPI_CTL_EMISO 0x00000100 /* Enable MISO */ -#define SPI_CTL_SIZE 0x00000600 /* Word Transfer Size */ -#define SPI_CTL_SIZE08 0x00000000 /* SIZE: 8 bits */ -#define SPI_CTL_SIZE16 0x00000200 /* SIZE: 16 bits */ -#define SPI_CTL_SIZE32 0x00000400 /* SIZE: 32 bits */ -#define SPI_CTL_LSBF 0x00001000 /* LSB First */ -#define SPI_CTL_FCEN 0x00002000 /* Flow-Control Enable */ -#define SPI_CTL_FCCH 0x00004000 /* Flow-Control Channel Selection */ -#define SPI_CTL_FCPL 0x00008000 /* Flow-Control Polarity */ -#define SPI_CTL_FCWM 0x00030000 /* Flow-Control Water-Mark */ -#define SPI_CTL_FIFO0 0x00000000 /* FCWM: TFIFO empty or RFIFO Full */ -#define SPI_CTL_FIFO1 0x00010000 /* FCWM: TFIFO 75% or more empty or RFIFO 75% or more full */ -#define SPI_CTL_FIFO2 0x00020000 /* FCWM: TFIFO 50% or more empty or RFIFO 50% or more full */ -#define SPI_CTL_FMODE 0x00040000 /* Fast-mode Enable */ -#define SPI_CTL_MIOM 0x00300000 /* Multiple I/O Mode */ -#define SPI_CTL_MIO_DIS 0x00000000 /* MIOM: Disable */ -#define SPI_CTL_MIO_DUAL 0x00100000 /* MIOM: Enable DIOM (Dual I/O Mode) */ -#define SPI_CTL_MIO_QUAD 0x00200000 /* MIOM: Enable QUAD (Quad SPI Mode) */ -#define SPI_CTL_SOSI 0x00400000 /* Start on MOSI */ -/* SPI_RX_CONTROL */ -#define SPI_RXCTL_REN 0x00000001 /* Receive Channel Enable */ -#define SPI_RXCTL_RTI 0x00000004 /* Receive Transfer Initiate */ -#define SPI_RXCTL_RWCEN 0x00000008 /* Receive Word Counter Enable */ -#define SPI_RXCTL_RDR 0x00000070 /* Receive Data Request */ -#define SPI_RXCTL_RDR_DIS 0x00000000 /* RDR: Disabled */ -#define SPI_RXCTL_RDR_NE 0x00000010 /* RDR: RFIFO not empty */ -#define SPI_RXCTL_RDR_25 0x00000020 /* RDR: RFIFO 25% full */ -#define SPI_RXCTL_RDR_50 0x00000030 /* RDR: RFIFO 50% full */ -#define SPI_RXCTL_RDR_75 0x00000040 /* RDR: RFIFO 75% full */ -#define SPI_RXCTL_RDR_FULL 0x00000050 /* RDR: RFIFO full */ -#define SPI_RXCTL_RDO 0x00000100 /* Receive Data Over-Run */ -#define SPI_RXCTL_RRWM 0x00003000 /* FIFO Regular Water-Mark */ -#define SPI_RXCTL_RWM_0 0x00000000 /* RRWM: RFIFO Empty */ -#define SPI_RXCTL_RWM_25 0x00001000 /* RRWM: RFIFO 25% full */ -#define SPI_RXCTL_RWM_50 0x00002000 /* RRWM: RFIFO 50% full */ -#define SPI_RXCTL_RWM_75 0x00003000 /* RRWM: RFIFO 75% full */ -#define SPI_RXCTL_RUWM 0x00070000 /* FIFO Urgent Water-Mark */ -#define SPI_RXCTL_UWM_DIS 0x00000000 /* RUWM: Disabled */ -#define SPI_RXCTL_UWM_25 0x00010000 /* RUWM: RFIFO 25% full */ -#define SPI_RXCTL_UWM_50 0x00020000 /* RUWM: RFIFO 50% full */ -#define SPI_RXCTL_UWM_75 0x00030000 /* RUWM: RFIFO 75% full */ -#define SPI_RXCTL_UWM_FULL 0x00040000 /* RUWM: RFIFO full */ -/* SPI_TX_CONTROL */ -#define SPI_TXCTL_TEN 0x00000001 /* Transmit Channel Enable */ -#define SPI_TXCTL_TTI 0x00000004 /* Transmit Transfer Initiate */ -#define SPI_TXCTL_TWCEN 0x00000008 /* Transmit Word Counter Enable */ -#define SPI_TXCTL_TDR 0x00000070 /* Transmit Data Request */ -#define SPI_TXCTL_TDR_DIS 0x00000000 /* TDR: Disabled */ -#define SPI_TXCTL_TDR_NF 0x00000010 /* TDR: TFIFO not full */ -#define SPI_TXCTL_TDR_25 0x00000020 /* TDR: TFIFO 25% empty */ -#define SPI_TXCTL_TDR_50 0x00000030 /* TDR: TFIFO 50% empty */ -#define SPI_TXCTL_TDR_75 0x00000040 /* TDR: TFIFO 75% empty */ -#define SPI_TXCTL_TDR_EMPTY 0x00000050 /* TDR: TFIFO empty */ -#define SPI_TXCTL_TDU 0x00000100 /* Transmit Data Under-Run */ -#define SPI_TXCTL_TRWM 0x00003000 /* FIFO Regular Water-Mark */ -#define SPI_TXCTL_RWM_FULL 0x00000000 /* TRWM: TFIFO full */ -#define SPI_TXCTL_RWM_25 0x00001000 /* TRWM: TFIFO 25% empty */ -#define SPI_TXCTL_RWM_50 0x00002000 /* TRWM: TFIFO 50% empty */ -#define SPI_TXCTL_RWM_75 0x00003000 /* TRWM: TFIFO 75% empty */ -#define SPI_TXCTL_TUWM 0x00070000 /* FIFO Urgent Water-Mark */ -#define SPI_TXCTL_UWM_DIS 0x00000000 /* TUWM: Disabled */ -#define SPI_TXCTL_UWM_25 0x00010000 /* TUWM: TFIFO 25% empty */ -#define SPI_TXCTL_UWM_50 0x00020000 /* TUWM: TFIFO 50% empty */ -#define SPI_TXCTL_UWM_75 0x00030000 /* TUWM: TFIFO 75% empty */ -#define SPI_TXCTL_UWM_EMPTY 0x00040000 /* TUWM: TFIFO empty */ -/* SPI_CLOCK */ -#define SPI_CLK_BAUD 0x0000FFFF /* Baud Rate */ -/* SPI_DELAY */ -#define SPI_DLY_STOP 0x000000FF /* Transfer delay time in multiples of SCK period */ -#define SPI_DLY_LEADX 0x00000100 /* Extended (1 SCK) LEAD Control */ -#define SPI_DLY_LAGX 0x00000200 /* Extended (1 SCK) LAG control */ -/* SPI_SSEL */ -#define SPI_SLVSEL_SSE1 0x00000002 /* SPISSEL1 Enable */ -#define SPI_SLVSEL_SSE2 0x00000004 /* SPISSEL2 Enable */ -#define SPI_SLVSEL_SSE3 0x00000008 /* SPISSEL3 Enable */ -#define SPI_SLVSEL_SSE4 0x00000010 /* SPISSEL4 Enable */ -#define SPI_SLVSEL_SSE5 0x00000020 /* SPISSEL5 Enable */ -#define SPI_SLVSEL_SSE6 0x00000040 /* SPISSEL6 Enable */ -#define SPI_SLVSEL_SSE7 0x00000080 /* SPISSEL7 Enable */ -#define SPI_SLVSEL_SSEL1 0x00000200 /* SPISSEL1 Value */ -#define SPI_SLVSEL_SSEL2 0x00000400 /* SPISSEL2 Value */ -#define SPI_SLVSEL_SSEL3 0x00000800 /* SPISSEL3 Value */ -#define SPI_SLVSEL_SSEL4 0x00001000 /* SPISSEL4 Value */ -#define SPI_SLVSEL_SSEL5 0x00002000 /* SPISSEL5 Value */ -#define SPI_SLVSEL_SSEL6 0x00004000 /* SPISSEL6 Value */ -#define SPI_SLVSEL_SSEL7 0x00008000 /* SPISSEL7 Value */ -/* SPI_RWC */ -#define SPI_RWC_VALUE 0x0000FFFF /* Received Word-Count */ -/* SPI_RWCR */ -#define SPI_RWCR_VALUE 0x0000FFFF /* Received Word-Count Reload */ -/* SPI_TWC */ -#define SPI_TWC_VALUE 0x0000FFFF /* Transmitted Word-Count */ -/* SPI_TWCR */ -#define SPI_TWCR_VALUE 0x0000FFFF /* Transmitted Word-Count Reload */ -/* SPI_IMASK */ -#define SPI_IMSK_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ -#define SPI_IMSK_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ -#define SPI_IMSK_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ -#define SPI_IMSK_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ -#define SPI_IMSK_RSM 0x00000100 /* Receive Start Interrupt Mask */ -#define SPI_IMSK_TSM 0x00000200 /* Transmit Start Interrupt Mask */ -#define SPI_IMSK_RFM 0x00000400 /* Receive Finish Interrupt Mask */ -#define SPI_IMSK_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ -/* SPI_IMASKCL */ -#define SPI_IMSK_CLR_RUW 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_CLR_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_CLR_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ -#define SPI_IMSK_CLR_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ -#define SPI_IMSK_CLR_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ -#define SPI_IMSK_CLR_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ -#define SPI_IMSK_CLR_RSM 0x00000100 /* Receive Start Interrupt Mask */ -#define SPI_IMSK_CLR_TSM 0x00000200 /* Transmit Start Interrupt Mask */ -#define SPI_IMSK_CLR_RFM 0x00000400 /* Receive Finish Interrupt Mask */ -#define SPI_IMSK_CLR_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ -/* SPI_IMASKST */ -#define SPI_IMSK_SET_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_SET_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_SET_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ -#define SPI_IMSK_SET_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ -#define SPI_IMSK_SET_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ -#define SPI_IMSK_SET_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ -#define SPI_IMSK_SET_RSM 0x00000100 /* Receive Start Interrupt Mask */ -#define SPI_IMSK_SET_TSM 0x00000200 /* Transmit Start Interrupt Mask */ -#define SPI_IMSK_SET_RFM 0x00000400 /* Receive Finish Interrupt Mask */ -#define SPI_IMSK_SET_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ -/* SPI_STATUS */ -#define SPI_STAT_SPIF 0x00000001 /* SPI Finished */ -#define SPI_STAT_RUWM 0x00000002 /* Receive Urgent Water-Mark Breached */ -#define SPI_STAT_TUWM 0x00000004 /* Transmit Urgent Water-Mark Breached */ -#define SPI_STAT_ROE 0x00000010 /* Receive Over-Run Error Indication */ -#define SPI_STAT_TUE 0x00000020 /* Transmit Under-Run Error Indication */ -#define SPI_STAT_TCE 0x00000040 /* Transmit Collision Error Indication */ -#define SPI_STAT_MODF 0x00000080 /* Mode Fault Error Indication */ -#define SPI_STAT_RS 0x00000100 /* Receive Start Indication */ -#define SPI_STAT_TS 0x00000200 /* Transmit Start Indication */ -#define SPI_STAT_RF 0x00000400 /* Receive Finish Indication */ -#define SPI_STAT_TF 0x00000800 /* Transmit Finish Indication */ -#define SPI_STAT_RFS 0x00007000 /* SPI_RFIFO status */ -#define SPI_STAT_RFIFO_EMPTY 0x00000000 /* RFS: RFIFO Empty */ -#define SPI_STAT_RFIFO_25 0x00001000 /* RFS: RFIFO 25% Full */ -#define SPI_STAT_RFIFO_50 0x00002000 /* RFS: RFIFO 50% Full */ -#define SPI_STAT_RFIFO_75 0x00003000 /* RFS: RFIFO 75% Full */ -#define SPI_STAT_RFIFO_FULL 0x00004000 /* RFS: RFIFO Full */ -#define SPI_STAT_TFS 0x00070000 /* SPI_TFIFO status */ -#define SPI_STAT_TFIFO_FULL 0x00000000 /* TFS: TFIFO full */ -#define SPI_STAT_TFIFO_25 0x00010000 /* TFS: TFIFO 25% empty */ -#define SPI_STAT_TFIFO_50 0x00020000 /* TFS: TFIFO 50% empty */ -#define SPI_STAT_TFIFO_75 0x00030000 /* TFS: TFIFO 75% empty */ -#define SPI_STAT_TFIFO_EMPTY 0x00040000 /* TFS: TFIFO empty */ -#define SPI_STAT_FCS 0x00100000 /* Flow-Control Stall Indication */ -#define SPI_STAT_RFE 0x00400000 /* SPI_RFIFO Empty */ -#define SPI_STAT_TFF 0x00800000 /* SPI_TFIFO Full */ -/* SPI_ILAT */ -#define SPI_ILAT_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ -#define SPI_ILAT_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ -#define SPI_ILAT_ROI 0x00000010 /* Receive Over-Run Error Indication */ -#define SPI_ILAT_TUI 0x00000020 /* Transmit Under-Run Error Indication */ -#define SPI_ILAT_TCI 0x00000040 /* Transmit Collision Error Indication */ -#define SPI_ILAT_MFI 0x00000080 /* Mode Fault Error Indication */ -#define SPI_ILAT_RSI 0x00000100 /* Receive Start Indication */ -#define SPI_ILAT_TSI 0x00000200 /* Transmit Start Indication */ -#define SPI_ILAT_RFI 0x00000400 /* Receive Finish Indication */ -#define SPI_ILAT_TFI 0x00000800 /* Transmit Finish Indication */ -/* SPI_ILATCL */ -#define SPI_ILAT_CLR_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ -#define SPI_ILAT_CLR_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ -#define SPI_ILAT_CLR_ROI 0x00000010 /* Receive Over-Run Error Indication */ -#define SPI_ILAT_CLR_TUI 0x00000020 /* Transmit Under-Run Error Indication */ -#define SPI_ILAT_CLR_TCI 0x00000040 /* Transmit Collision Error Indication */ -#define SPI_ILAT_CLR_MFI 0x00000080 /* Mode Fault Error Indication */ -#define SPI_ILAT_CLR_RSI 0x00000100 /* Receive Start Indication */ -#define SPI_ILAT_CLR_TSI 0x00000200 /* Transmit Start Indication */ -#define SPI_ILAT_CLR_RFI 0x00000400 /* Receive Finish Indication */ -#define SPI_ILAT_CLR_TFI 0x00000800 /* Transmit Finish Indication */ - -/* - * adi spi3 registers layout - */ -struct adi_spi_regs { - u32 revid; - u32 control; - u32 rx_control; - u32 tx_control; - u32 clock; - u32 delay; - u32 ssel; - u32 rwc; - u32 rwcr; - u32 twc; - u32 twcr; - u32 reserved0; - u32 emask; - u32 emaskcl; - u32 emaskst; - u32 reserved1; - u32 status; - u32 elat; - u32 elatcl; - u32 reserved2; - u32 rfifo; - u32 reserved3; - u32 tfifo; -}; - -#define MAX_CTRL_CS 8 /* cs in spi controller */ - -/* device.platform_data for SSP controller devices */ -struct adi_spi3_master { - u16 num_chipselect; - u16 pin_req[7]; -}; - -/* spi_board_info.controller_data for SPI slave devices, - * copied to spi_device.platform_data ... mostly for dma tuning - */ -struct adi_spi3_chip { - u32 control; - u16 cs_chg_udelay; /* Some devices require 16-bit delays */ - u32 tx_dummy_val; /* tx value for rx only transfer */ - bool enable_dma; -}; - -#endif /* _ADI_SPI3_H_ */ From c87bdcc89d867df13fbb92d05323337bfd15d579 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:31 +0200 Subject: [PATCH 02/40] spi: spi-fsl-dspi: Drop unreachable else if statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The if statement just above this if/else statement triggers on the same condition, and then invalidates it. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 0630962ce442..3ca9b9608801 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -593,8 +593,7 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) dspi_pushr |= SPI_PUSHR_EOQ; if ((dspi->cs_change) && (!dspi->len)) dspi_pushr &= ~SPI_PUSHR_CONT; - } else if (tx_word && (dspi->len == 1)) - dspi_pushr |= SPI_PUSHR_EOQ; + } regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); From 4779f23d1ac8694f7acc537fcadb750bff664ea5 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:32 +0200 Subject: [PATCH 03/40] spi: spi-fsl-dspi: Drop unneeded use of dataflags bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking directly against pointer value should be at least as fast as doing bitmasking and compare, so let's keep it simple. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 3ca9b9608801..3bf135bf8b93 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -38,8 +38,6 @@ #define DRIVER_NAME "fsl-dspi" -#define TRAN_STATE_RX_VOID 0x01 -#define TRAN_STATE_TX_VOID 0x02 #define TRAN_STATE_WORD_ODD_NUM 0x04 #define DSPI_FIFO_SIZE 4 @@ -232,7 +230,7 @@ static void dspi_rx_dma_callback(void *arg) rx_word = is_double_byte_mode(dspi); - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) { + if (dspi->rx) { for (i = 0; i < dma->curr_xfer_len; i++) { d = dspi->dma->rx_dma_buf[i]; rx_word ? (*(u16 *)dspi->rx = d) : @@ -538,12 +536,13 @@ static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) { u16 d16; - if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) + if (dspi->tx) { d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; - else + dspi->tx += tx_word + 1; + } else { d16 = dspi->void_write_data; + } - dspi->tx += tx_word + 1; dspi->len -= tx_word + 1; return SPI_PUSHR_TXDATA(d16) | @@ -560,10 +559,10 @@ static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) regmap_read(dspi->regmap, SPI_POPR, &val); d = SPI_POPR_RXDATA(val); - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + if (dspi->rx) { rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d); - - dspi->rx += rx_word + 1; + dspi->rx += rx_word + 1; + } } static int dspi_eoq_write(struct fsl_dspi *dspi) @@ -687,12 +686,6 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->rx_end = dspi->rx + transfer->len; dspi->len = transfer->len; - if (!dspi->rx) - dspi->dataflags |= TRAN_STATE_RX_VOID; - - if (!dspi->tx) - dspi->dataflags |= TRAN_STATE_TX_VOID; - regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, From 9e1dc9bd099363760afdd2316fc057f9ca38b559 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:33 +0200 Subject: [PATCH 04/40] spi: spi-fsl-dspi: Fix per transfer cs_change handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of 92dc20d83adec565378254c0630e839ff5674e14, transfer->cs_change has been supported for non-last transfers, but not for last transfer. This change brings handling of cs_change in line with the specification in spi.h, implementing handling of transfer->cs_change for all transfers. The value for CMD FIFO is precalculated with transfer->cs_change field taken into account, allowing for CS de-activate between transfers and keeping CS activated after last transfer. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 68 ++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 3bf135bf8b93..c0c3b8bf2781 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -84,11 +84,16 @@ #define SPI_RSER_TCFQE 0x80000000 #define SPI_PUSHR 0x34 -#define SPI_PUSHR_CONT (1 << 31) -#define SPI_PUSHR_CTAS(x) (((x) & 0x00000003) << 28) -#define SPI_PUSHR_EOQ (1 << 27) -#define SPI_PUSHR_CTCNT (1 << 26) -#define SPI_PUSHR_PCS(x) (((1 << x) & 0x0000003f) << 16) +#define SPI_PUSHR_CMD_CONT (1 << 15) +#define SPI_PUSHR_CONT (SPI_PUSHR_CMD_CONT << 16) +#define SPI_PUSHR_CMD_CTAS(x) (((x) & 0x0003) << 12) +#define SPI_PUSHR_CTAS(x) (SPI_PUSHR_CMD_CTAS(x) << 16) +#define SPI_PUSHR_CMD_EOQ (1 << 11) +#define SPI_PUSHR_EOQ (SPI_PUSHR_CMD_EOQ << 16) +#define SPI_PUSHR_CMD_CTCNT (1 << 10) +#define SPI_PUSHR_CTCNT (SPI_PUSHR_CMD_CTCNT << 16) +#define SPI_PUSHR_CMD_PCS(x) ((1 << x) & 0x003f) +#define SPI_PUSHR_PCS(x) (SPI_PUSHR_CMD_PCS(x) << 16) #define SPI_PUSHR_TXDATA(x) ((x) & 0x0000ffff) #define SPI_PUSHR_SLAVE 0x34 @@ -189,9 +194,8 @@ struct fsl_dspi { void *rx; void *rx_end; char dataflags; - u8 cs; u16 void_write_data; - u32 cs_change; + u16 tx_cmd; const struct fsl_dspi_devtype_data *devtype_data; wait_queue_head_t waitq; @@ -254,8 +258,6 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) for (i = 0; i < dma->curr_xfer_len; i++) { dspi->dma->tx_dma_buf[i] = dspi_data_to_pushr(dspi, tx_word); - if ((dspi->cs_change) && (!dspi->len)) - dspi->dma->tx_dma_buf[i] &= ~SPI_PUSHR_CONT; } dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx, @@ -534,21 +536,22 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns, static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) { - u16 d16; + u16 data, cmd; if (dspi->tx) { - d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; + data = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; dspi->tx += tx_word + 1; } else { - d16 = dspi->void_write_data; + data = dspi->void_write_data; } dspi->len -= tx_word + 1; - return SPI_PUSHR_TXDATA(d16) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(0) | - SPI_PUSHR_CONT; + cmd = dspi->tx_cmd; + if (dspi->len > 0) + cmd |= SPI_PUSHR_CMD_CONT; + + return (cmd << 16) | SPI_PUSHR_TXDATA(data); } static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) @@ -587,12 +590,9 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) { - /* last transfer in the transfer */ + if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) + /* request EOQ flag for last transfer in queue */ dspi_pushr |= SPI_PUSHR_EOQ; - if ((dspi->cs_change) && (!dspi->len)) - dspi_pushr &= ~SPI_PUSHR_CONT; - } regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); @@ -635,9 +635,6 @@ static int dspi_tcfq_write(struct fsl_dspi *dspi) dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - if ((dspi->cs_change) && (!dspi->len)) - dspi_pushr &= ~SPI_PUSHR_CONT; - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); return tx_word + 1; @@ -672,11 +669,26 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->cur_transfer = transfer; dspi->cur_msg = message; dspi->cur_chip = spi_get_ctldata(spi); - dspi->cs = spi->chip_select; - dspi->cs_change = 0; + /* Prepare command word for CMD FIFO */ + dspi->tx_cmd = SPI_PUSHR_CMD_CTAS(0) | + SPI_PUSHR_CMD_PCS(spi->chip_select); if (list_is_last(&dspi->cur_transfer->transfer_list, - &dspi->cur_msg->transfers) || transfer->cs_change) - dspi->cs_change = 1; + &dspi->cur_msg->transfers)) { + /* Leave PCS activated after last transfer when + * cs_change is set. + */ + if (transfer->cs_change) + dspi->tx_cmd |= SPI_PUSHR_CMD_CONT; + } else { + /* Keep PCS active between transfers in same message + * when cs_change is not set, and de-activate PCS + * between transfers in the same message when + * cs_change is set. + */ + if (!transfer->cs_change) + dspi->tx_cmd |= SPI_PUSHR_CMD_CONT; + } + dspi->void_write_data = dspi->cur_chip->void_write_data; dspi->dataflags = 0; From 0a4ec2c158634afa208ceda970b2c83764a1d99c Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:34 +0200 Subject: [PATCH 05/40] spi: spi-fsl-dspi: Simplify transfer counter handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify driver by avoiding counter wrapping by clearing transfer counter on first SPI transfer per interrupt instead of tracking what it was before. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 39 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index c0c3b8bf2781..2371b9978e65 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -119,8 +119,6 @@ #define SPI_CS_ASSERT 0x02 #define SPI_CS_DROP 0x04 -#define SPI_TCR_TCNT_MAX 0x10000 - #define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) struct chip_data { @@ -201,7 +199,6 @@ struct fsl_dspi { wait_queue_head_t waitq; u32 waitflags; - u32 spi_tcnt; struct fsl_dspi_dma *dma; }; @@ -594,6 +591,10 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) /* request EOQ flag for last transfer in queue */ dspi_pushr |= SPI_PUSHR_EOQ; + /* Clear transfer counter on first transfer (in FIFO) */ + if (tx_count == 0) + dspi_pushr |= SPI_PUSHR_CTCNT; + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); tx_count++; @@ -635,6 +636,9 @@ static int dspi_tcfq_write(struct fsl_dspi *dspi) dspi_pushr = dspi_data_to_pushr(dspi, tx_word); + /* Clear transfer counter on each transfer */ + dspi_pushr |= SPI_PUSHR_CTCNT; + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); return tx_word + 1; @@ -658,10 +662,6 @@ static int dspi_transfer_one_message(struct spi_master *master, struct spi_transfer *transfer; int status = 0; enum dspi_trans_mode trans_mode; - u32 spi_tcr; - - regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); - dspi->spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); message->actual_length = 0; @@ -831,7 +831,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) struct spi_message *msg = dspi->cur_msg; enum dspi_trans_mode trans_mode; u32 spi_sr, spi_tcr; - u32 spi_tcnt, tcnt_diff; + u16 spi_tcnt; int tx_word; regmap_read(dspi->regmap, SPI_SR, &spi_sr); @@ -841,26 +841,15 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) { tx_word = is_double_byte_mode(dspi); + /* Get transfer counter (in number of SPI transfers). It was + * reset to 0 when transfer(s) were started. + */ regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); - /* - * The width of SPI Transfer Counter in SPI_TCR is 16bits, - * so the max couner is 65535. When the counter reach 65535, - * it will wrap around, counter reset to zero. - * spi_tcnt my be less than dspi->spi_tcnt, it means the - * counter already wrapped around. - * SPI Transfer Counter is a counter of transmitted frames. - * The size of frame maybe two bytes. - */ - tcnt_diff = ((spi_tcnt + SPI_TCR_TCNT_MAX) - dspi->spi_tcnt) - % SPI_TCR_TCNT_MAX; - tcnt_diff *= (tx_word + 1); - if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) - tcnt_diff--; - msg->actual_length += tcnt_diff; - - dspi->spi_tcnt = spi_tcnt; + /* Update total number of bytes that were transferred */ + msg->actual_length += spi_tcnt * (tx_word + 1) - + (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM ? 1 : 0); trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { From dadcf4abd60ba6401b592c329a19719a6e1dd444 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:35 +0200 Subject: [PATCH 06/40] spi: spi-fsl-dspi: Support 4 to 16 bits per word transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This extends the driver with support for all SPI framesizes from 4 to 16 bits, and adds support for per transfer specific bits_per_word, while at the same time reducing code size and complexity. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 263 +++++++++++++------------------------ 1 file changed, 94 insertions(+), 169 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 2371b9978e65..df07dd4722fb 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -38,8 +38,6 @@ #define DRIVER_NAME "fsl-dspi" -#define TRAN_STATE_WORD_ODD_NUM 0x04 - #define DSPI_FIFO_SIZE 4 #define DSPI_DMA_BUFSIZE (DSPI_FIFO_SIZE * 1024) @@ -187,13 +185,13 @@ struct fsl_dspi { struct spi_message *cur_msg; struct chip_data *cur_chip; size_t len; - void *tx; - void *tx_end; + const void *tx; void *rx; void *rx_end; - char dataflags; u16 void_write_data; u16 tx_cmd; + u8 bits_per_word; + u8 bytes_per_word; const struct fsl_dspi_devtype_data *devtype_data; wait_queue_head_t waitq; @@ -202,15 +200,43 @@ struct fsl_dspi { struct fsl_dspi_dma *dma; }; -static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word); - -static inline int is_double_byte_mode(struct fsl_dspi *dspi) +static u16 dspi_pop_tx(struct fsl_dspi *dspi) { - unsigned int val; + u16 txdata = 0; - regmap_read(dspi->regmap, SPI_CTAR(0), &val); + if (dspi->tx) { + if (dspi->bytes_per_word == 1) + txdata = *(u8 *)dspi->tx; + else /* dspi->bytes_per_word == 2 */ + txdata = *(u16 *)dspi->tx; + dspi->tx += dspi->bytes_per_word; + } + dspi->len -= dspi->bytes_per_word; + return txdata; +} - return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1; +static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi) +{ + u16 cmd = dspi->tx_cmd, data = dspi_pop_tx(dspi); + + if (dspi->len > 0) + cmd |= SPI_PUSHR_CMD_CONT; + return cmd << 16 | data; +} + +static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata) +{ + if (!dspi->rx) + return; + + /* Mask of undefined bits */ + rxdata &= (1 << dspi->bits_per_word) - 1; + + if (dspi->bytes_per_word == 1) + *(u8 *)dspi->rx = rxdata; + else /* dspi->bytes_per_word == 2 */ + *(u16 *)dspi->rx = rxdata; + dspi->rx += dspi->bytes_per_word; } static void dspi_tx_dma_callback(void *arg) @@ -225,19 +251,11 @@ static void dspi_rx_dma_callback(void *arg) { struct fsl_dspi *dspi = arg; struct fsl_dspi_dma *dma = dspi->dma; - int rx_word; int i; - u16 d; - - rx_word = is_double_byte_mode(dspi); if (dspi->rx) { - for (i = 0; i < dma->curr_xfer_len; i++) { - d = dspi->dma->rx_dma_buf[i]; - rx_word ? (*(u16 *)dspi->rx = d) : - (*(u8 *)dspi->rx = d); - dspi->rx += rx_word + 1; - } + for (i = 0; i < dma->curr_xfer_len; i++) + dspi_push_rx(dspi, dspi->dma->rx_dma_buf[i]); } complete(&dma->cmd_rx_complete); @@ -248,14 +266,10 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) struct fsl_dspi_dma *dma = dspi->dma; struct device *dev = &dspi->pdev->dev; int time_left; - int tx_word; int i; - tx_word = is_double_byte_mode(dspi); - - for (i = 0; i < dma->curr_xfer_len; i++) { - dspi->dma->tx_dma_buf[i] = dspi_data_to_pushr(dspi, tx_word); - } + for (i = 0; i < dma->curr_xfer_len; i++) + dspi->dma->tx_dma_buf[i] = dspi_pop_tx_pushr(dspi); dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx, dma->tx_dma_phys, @@ -326,16 +340,14 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) struct device *dev = &dspi->pdev->dev; int curr_remaining_bytes; int bytes_per_buffer; - int word = 1; int ret = 0; - if (is_double_byte_mode(dspi)) - word = 2; curr_remaining_bytes = dspi->len; bytes_per_buffer = DSPI_DMA_BUFSIZE / DSPI_FIFO_SIZE; while (curr_remaining_bytes) { /* Check if current transfer fits the DMA buffer */ - dma->curr_xfer_len = curr_remaining_bytes / word; + dma->curr_xfer_len = curr_remaining_bytes + / dspi->bytes_per_word; if (dma->curr_xfer_len > bytes_per_buffer) dma->curr_xfer_len = bytes_per_buffer; @@ -345,7 +357,8 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) goto exit; } else { - curr_remaining_bytes -= dma->curr_xfer_len * word; + curr_remaining_bytes -= dma->curr_xfer_len + * dspi->bytes_per_word; if (curr_remaining_bytes < 0) curr_remaining_bytes = 0; } @@ -531,127 +544,56 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns, } } -static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) +static void fifo_write(struct fsl_dspi *dspi) { - u16 data, cmd; - - if (dspi->tx) { - data = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; - dspi->tx += tx_word + 1; - } else { - data = dspi->void_write_data; - } - - dspi->len -= tx_word + 1; - - cmd = dspi->tx_cmd; - if (dspi->len > 0) - cmd |= SPI_PUSHR_CMD_CONT; - - return (cmd << 16) | SPI_PUSHR_TXDATA(data); + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pop_tx_pushr(dspi)); } -static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) +static void dspi_tcfq_write(struct fsl_dspi *dspi) { - u16 d; - unsigned int val; - - regmap_read(dspi->regmap, SPI_POPR, &val); - d = SPI_POPR_RXDATA(val); - - if (dspi->rx) { - rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d); - dspi->rx += rx_word + 1; - } + /* Clear transfer count */ + dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT; + /* Write one entry to both TX FIFO and CMD FIFO simultaneously */ + fifo_write(dspi); } -static int dspi_eoq_write(struct fsl_dspi *dspi) +static u32 fifo_read(struct fsl_dspi *dspi) { - int tx_count = 0; - int tx_word; - u32 dspi_pushr = 0; + u32 rxdata = 0; - tx_word = is_double_byte_mode(dspi); - - while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) { - /* If we are in word mode, only have a single byte to transfer - * switch to byte mode temporarily. Will switch back at the - * end of the transfer. - */ - if (tx_word && (dspi->len == 1)) { - dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - regmap_update_bits(dspi->regmap, SPI_CTAR(0), - SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); - tx_word = 0; - } - - dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - - if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) - /* request EOQ flag for last transfer in queue */ - dspi_pushr |= SPI_PUSHR_EOQ; - - /* Clear transfer counter on first transfer (in FIFO) */ - if (tx_count == 0) - dspi_pushr |= SPI_PUSHR_CTCNT; - - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); - - tx_count++; - } - - return tx_count * (tx_word + 1); -} - -static int dspi_eoq_read(struct fsl_dspi *dspi) -{ - int rx_count = 0; - int rx_word = is_double_byte_mode(dspi); - - while ((dspi->rx < dspi->rx_end) - && (rx_count < DSPI_FIFO_SIZE)) { - if (rx_word && (dspi->rx_end - dspi->rx) == 1) - rx_word = 0; - - dspi_data_from_popr(dspi, rx_word); - rx_count++; - } - - return rx_count; -} - -static int dspi_tcfq_write(struct fsl_dspi *dspi) -{ - int tx_word; - u32 dspi_pushr = 0; - - tx_word = is_double_byte_mode(dspi); - - if (tx_word && (dspi->len == 1)) { - dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - regmap_update_bits(dspi->regmap, SPI_CTAR(0), - SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); - tx_word = 0; - } - - dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - - /* Clear transfer counter on each transfer */ - dspi_pushr |= SPI_PUSHR_CTCNT; - - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); - - return tx_word + 1; + regmap_read(dspi->regmap, SPI_POPR, &rxdata); + return rxdata; } static void dspi_tcfq_read(struct fsl_dspi *dspi) { - int rx_word = is_double_byte_mode(dspi); + dspi_push_rx(dspi, fifo_read(dspi)); +} - if (rx_word && (dspi->rx_end - dspi->rx) == 1) - rx_word = 0; +static void dspi_eoq_write(struct fsl_dspi *dspi) +{ + int fifo_size = DSPI_FIFO_SIZE; - dspi_data_from_popr(dspi, rx_word); + /* Fill TX FIFO with as many transfers as possible */ + while (dspi->len && fifo_size--) { + /* Request EOQF for last transfer in FIFO */ + if (dspi->len == dspi->bytes_per_word || fifo_size == 0) + dspi->tx_cmd |= SPI_PUSHR_CMD_EOQ; + /* Clear transfer count for first transfer in FIFO */ + if (fifo_size == (DSPI_FIFO_SIZE - 1)) + dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT; + /* Write combined TX FIFO and CMD FIFO entry */ + fifo_write(dspi); + } +} + +static void dspi_eoq_read(struct fsl_dspi *dspi) +{ + int fifo_size = DSPI_FIFO_SIZE; + + /* Read one FIFO entry at and push to rx buffer */ + while ((dspi->rx < dspi->rx_end) && fifo_size--) + dspi_push_rx(dspi, fifo_read(dspi)); } static int dspi_transfer_one_message(struct spi_master *master, @@ -691,19 +633,24 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->void_write_data = dspi->cur_chip->void_write_data; - dspi->dataflags = 0; - dspi->tx = (void *)transfer->tx_buf; - dspi->tx_end = dspi->tx + transfer->len; + dspi->tx = transfer->tx_buf; dspi->rx = transfer->rx_buf; dspi->rx_end = dspi->rx + transfer->len; dspi->len = transfer->len; + /* Validated transfer specific frame size (defaults applied) */ + dspi->bits_per_word = transfer->bits_per_word; + if (transfer->bits_per_word <= 8) + dspi->bytes_per_word = 1; + else + dspi->bytes_per_word = 2; regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); regmap_write(dspi->regmap, SPI_CTAR(0), - dspi->cur_chip->ctar_val); + dspi->cur_chip->ctar_val | + SPI_FRAME_BITS(transfer->bits_per_word)); trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { @@ -754,16 +701,9 @@ static int dspi_setup(struct spi_device *spi) struct fsl_dspi_platform_data *pdata; u32 cs_sck_delay = 0, sck_cs_delay = 0; unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0; - unsigned char pasc = 0, asc = 0, fmsz = 0; + unsigned char pasc = 0, asc = 0; unsigned long clkrate; - if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) { - fmsz = spi->bits_per_word - 1; - } else { - pr_err("Invalid wordsize\n"); - return -ENODEV; - } - /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (chip == NULL) { @@ -799,8 +739,7 @@ static int dspi_setup(struct spi_device *spi) /* Set After SCK delay scale values */ ns_delay_scale(&pasc, &asc, sck_cs_delay, clkrate); - chip->ctar_val = SPI_CTAR_FMSZ(fmsz) - | SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0) + chip->ctar_val = SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0) | SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0) | SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0) | SPI_CTAR_PCSSCK(pcssck) @@ -832,24 +771,19 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) enum dspi_trans_mode trans_mode; u32 spi_sr, spi_tcr; u16 spi_tcnt; - int tx_word; regmap_read(dspi->regmap, SPI_SR, &spi_sr); regmap_write(dspi->regmap, SPI_SR, spi_sr); if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) { - tx_word = is_double_byte_mode(dspi); - /* Get transfer counter (in number of SPI transfers). It was * reset to 0 when transfer(s) were started. */ regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); - /* Update total number of bytes that were transferred */ - msg->actual_length += spi_tcnt * (tx_word + 1) - - (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM ? 1 : 0); + msg->actual_length += spi_tcnt * dspi->bytes_per_word; trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { @@ -866,14 +800,6 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) } if (!dspi->len) { - if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) { - regmap_update_bits(dspi->regmap, - SPI_CTAR(0), - SPI_FRAME_BITS_MASK, - SPI_FRAME_BITS(16)); - dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM; - } - dspi->waitflags = 1; wake_up_interruptible(&dspi->waitq); } else { @@ -973,8 +899,7 @@ static int dspi_probe(struct platform_device *pdev) master->cleanup = dspi_cleanup; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; - master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) | - SPI_BPW_MASK(16); + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); pdata = dev_get_platdata(&pdev->dev); if (pdata) { From d87e08f1421373f010308b1d065a1f0c3b251a52 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:37 +0200 Subject: [PATCH 07/40] spi: spi-fsl-dspi: Fix MCR register handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MCR register is not changed, so initialize it in dspi_init(). The exception is the CLR_TXF and CLR_RXF bits, which should be written to before each transfer to make sure we start with empty FIFOs. With MCR register now configured as volatile, the regmap_update_bits will do a real read-modify-write cycle. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index df07dd4722fb..39b3c6f4f5c3 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -120,7 +120,6 @@ #define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) struct chip_data { - u32 mcr_val; u32 ctar_val; u16 void_write_data; }; @@ -644,10 +643,9 @@ static int dspi_transfer_one_message(struct spi_master *master, else dspi->bytes_per_word = 2; - regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); regmap_update_bits(dspi->regmap, SPI_MCR, - SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, - SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); regmap_write(dspi->regmap, SPI_CTAR(0), dspi->cur_chip->ctar_val | SPI_FRAME_BITS(transfer->bits_per_word)); @@ -725,9 +723,6 @@ static int dspi_setup(struct spi_device *spi) sck_cs_delay = pdata->sck_cs_delay; } - chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS | - SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; - chip->void_write_data = 0; clkrate = clk_get_rate(dspi->clk); @@ -871,6 +866,7 @@ static const struct regmap_config dspi_regmap_config = { static void dspi_init(struct fsl_dspi *dspi) { + regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS); regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR); } From 58ba07ec79e62054dd2b2f1cea082a542aa01c44 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:38 +0200 Subject: [PATCH 08/40] spi: spi-fsl-dspi: Add support for XSPI mode registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares for adding support for extended SPI mode (XSPI), by extending the regmap with the extra SREX and CTAREx registers. An additional register map is made for allowing 16 bit access to CMD and TX FIFO of the PUSHR register separately, which is also needed for XSPI mode support. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 63 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index d83d3496d538..3e9dd645ee54 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -108,11 +108,21 @@ #define SPI_RXFR2 0x84 #define SPI_RXFR3 0x88 +#define SPI_CTARE(x) (0x11c + (((x) & 0x3) * 4)) +#define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16) +#define SPI_CTARE_DTCP(x) ((x) & 0x7ff) + +#define SPI_SREX 0x13c + #define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1) #define SPI_FRAME_BITS_MASK SPI_CTAR_FMSZ(0xf) #define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf) #define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7) +/* Register offsets for regmap_pushr */ +#define PUSHR_CMD 0x0 +#define PUSHR_TX 0x2 + #define SPI_CS_INIT 0x01 #define SPI_CS_ASSERT 0x02 #define SPI_CS_DROP 0x04 @@ -133,6 +143,7 @@ enum dspi_trans_mode { struct fsl_dspi_devtype_data { enum dspi_trans_mode trans_mode; u8 max_clock_factor; + bool xspi_mode; }; static const struct fsl_dspi_devtype_data vf610_data = { @@ -143,6 +154,7 @@ static const struct fsl_dspi_devtype_data vf610_data = { static const struct fsl_dspi_devtype_data ls1021a_v1_data = { .trans_mode = DSPI_TCFQ_MODE, .max_clock_factor = 8, + .xspi_mode = true, }; static const struct fsl_dspi_devtype_data ls2085a_data = { @@ -177,6 +189,7 @@ struct fsl_dspi { struct platform_device *pdev; struct regmap *regmap; + struct regmap *regmap_pushr; int irq; struct clk *clk; @@ -876,6 +889,35 @@ static const struct regmap_config dspi_regmap_config = { .volatile_table = &dspi_volatile_table, }; +static const struct regmap_range dspi_xspi_volatile_ranges[] = { + regmap_reg_range(SPI_MCR, SPI_TCR), + regmap_reg_range(SPI_SR, SPI_SR), + regmap_reg_range(SPI_PUSHR, SPI_RXFR3), + regmap_reg_range(SPI_SREX, SPI_SREX), +}; + +static const struct regmap_access_table dspi_xspi_volatile_table = { + .yes_ranges = dspi_xspi_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dspi_xspi_volatile_ranges), +}; + +static const struct regmap_config dspi_xspi_regmap_config[] = { + { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x13c, + .volatile_table = &dspi_xspi_volatile_table, + }, + { + .name = "pushr", + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 2, + .max_register = 0x2, + }, +}; + static void dspi_init(struct fsl_dspi *dspi) { regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS); @@ -888,6 +930,7 @@ static int dspi_probe(struct platform_device *pdev) struct spi_master *master; struct fsl_dspi *dspi; struct resource *res; + const struct regmap_config *regmap_config; void __iomem *base; struct fsl_dspi_platform_data *pdata; int ret = 0, cs_num, bus_num; @@ -946,8 +989,11 @@ static int dspi_probe(struct platform_device *pdev) goto out_master_put; } - dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, - &dspi_regmap_config); + if (dspi->devtype_data->xspi_mode) + regmap_config = &dspi_xspi_regmap_config[0]; + else + regmap_config = &dspi_regmap_config; + dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base, regmap_config); if (IS_ERR(dspi->regmap)) { dev_err(&pdev->dev, "failed to init regmap: %ld\n", PTR_ERR(dspi->regmap)); @@ -955,6 +1001,19 @@ static int dspi_probe(struct platform_device *pdev) goto out_master_put; } + if (dspi->devtype_data->xspi_mode) { + dspi->regmap_pushr = devm_regmap_init_mmio( + &pdev->dev, base + SPI_PUSHR, + &dspi_xspi_regmap_config[1]); + if (IS_ERR(dspi->regmap_pushr)) { + dev_err(&pdev->dev, + "failed to init pushr regmap: %ld\n", + PTR_ERR(dspi->regmap_pushr)); + ret = PTR_ERR(dspi->regmap); + goto out_master_put; + } + } + dspi_init(dspi); dspi->irq = platform_get_irq(pdev, 0); if (dspi->irq < 0) { From 51d583ae7792cb11e952124cedc0e06e7b55d031 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:39 +0200 Subject: [PATCH 09/40] spi: spi-fsl-dspi: Framesize control for XSPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 3e9dd645ee54..ba83ff4512c9 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -119,6 +119,9 @@ #define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf) #define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7) +#define SPI_FRAME_EBITS(bits) SPI_CTARE_FMSZE(((bits) - 1) >> 4) +#define SPI_FRAME_EBITS_MASK SPI_CTARE_FMSZE(1) + /* Register offsets for regmap_pushr */ #define PUSHR_CMD 0x0 #define PUSHR_TX 0x2 @@ -662,6 +665,10 @@ static int dspi_transfer_one_message(struct spi_master *master, regmap_write(dspi->regmap, SPI_CTAR(0), dspi->cur_chip->ctar_val | SPI_FRAME_BITS(transfer->bits_per_word)); + if (dspi->devtype_data->xspi_mode) + regmap_write(dspi->regmap, SPI_CTARE(0), + SPI_FRAME_EBITS(transfer->bits_per_word) + | SPI_CTARE_DTCP(1)); trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { @@ -922,6 +929,9 @@ static void dspi_init(struct fsl_dspi *dspi) { regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS); regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR); + if (dspi->devtype_data->xspi_mode) + regmap_write(dspi->regmap, SPI_CTARE(0), + SPI_CTARE_FMSZE(0) | SPI_CTARE_DTCP(1)); } static int dspi_probe(struct platform_device *pdev) From 8fcd151d2619c46e3435e891379a22985538633c Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:40 +0200 Subject: [PATCH 10/40] spi: spi-fsl-dspi: XSPI FIFO handling (in TCFQ mode) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements handling of split CMD and TX FIFO queues for XSPI when running in TCFQ mode. It should be simple to add it to EOQ mode also. Currently, EOQ mode is only used with coldfire. So if coldfire DSPI supports XSPI, XSPI FIFO handling should be added to EOQ mode also. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 55 +++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index ba83ff4512c9..67cd2e901255 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -215,15 +215,17 @@ struct fsl_dspi { struct fsl_dspi_dma *dma; }; -static u16 dspi_pop_tx(struct fsl_dspi *dspi) +static u32 dspi_pop_tx(struct fsl_dspi *dspi) { - u16 txdata = 0; + u32 txdata = 0; if (dspi->tx) { if (dspi->bytes_per_word == 1) txdata = *(u8 *)dspi->tx; - else /* dspi->bytes_per_word == 2 */ + else if (dspi->bytes_per_word == 2) txdata = *(u16 *)dspi->tx; + else /* dspi->bytes_per_word == 4 */ + txdata = *(u32 *)dspi->tx; dspi->tx += dspi->bytes_per_word; } dspi->len -= dspi->bytes_per_word; @@ -249,8 +251,10 @@ static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata) if (dspi->bytes_per_word == 1) *(u8 *)dspi->rx = rxdata; - else /* dspi->bytes_per_word == 2 */ + else if (dspi->bytes_per_word == 2) *(u16 *)dspi->rx = rxdata; + else /* dspi->bytes_per_word == 4 */ + *(u32 *)dspi->rx = rxdata; dspi->rx += dspi->bytes_per_word; } @@ -564,12 +568,47 @@ static void fifo_write(struct fsl_dspi *dspi) regmap_write(dspi->regmap, SPI_PUSHR, dspi_pop_tx_pushr(dspi)); } +static void cmd_fifo_write(struct fsl_dspi *dspi) +{ + u16 cmd = dspi->tx_cmd; + + if (dspi->len > 0) + cmd |= SPI_PUSHR_CMD_CONT; + regmap_write(dspi->regmap_pushr, PUSHR_CMD, cmd); +} + +static void tx_fifo_write(struct fsl_dspi *dspi, u16 txdata) +{ + regmap_write(dspi->regmap_pushr, PUSHR_TX, txdata); +} + static void dspi_tcfq_write(struct fsl_dspi *dspi) { /* Clear transfer count */ dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT; - /* Write one entry to both TX FIFO and CMD FIFO simultaneously */ - fifo_write(dspi); + + if (dspi->devtype_data->xspi_mode && dspi->bits_per_word > 16) { + /* Write two TX FIFO entries first, and then the corresponding + * CMD FIFO entry. + */ + u32 data = dspi_pop_tx(dspi); + + if (dspi->cur_chip->ctar_val & SPI_CTAR_LSBFE(1)) { + /* LSB */ + tx_fifo_write(dspi, data & 0xFFFF); + tx_fifo_write(dspi, data >> 16); + } else { + /* MSB */ + tx_fifo_write(dspi, data >> 16); + tx_fifo_write(dspi, data & 0xFFFF); + } + cmd_fifo_write(dspi); + } else { + /* Write one entry to both TX FIFO and CMD FIFO + * simultaneously. + */ + fifo_write(dspi); + } } static u32 fifo_read(struct fsl_dspi *dspi) @@ -656,8 +695,10 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->bits_per_word = transfer->bits_per_word; if (transfer->bits_per_word <= 8) dspi->bytes_per_word = 1; - else + else if (transfer->bits_per_word <= 16) dspi->bytes_per_word = 2; + else + dspi->bytes_per_word = 4; regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, From 35c9d461a436101a31d4ece7e5de5f4df6b59a5e Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:41 +0200 Subject: [PATCH 11/40] spi: spi-fsl-dspi: Advertise 32 bit for XSPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 67cd2e901255..eed55491b2c9 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1001,7 +1001,6 @@ static int dspi_probe(struct platform_device *pdev) master->cleanup = dspi_cleanup; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); pdata = dev_get_platdata(&pdev->dev); if (pdata) { @@ -1033,6 +1032,11 @@ static int dspi_probe(struct platform_device *pdev) } } + if (dspi->devtype_data->xspi_mode) + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + else + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) { From 3e7cc6252dc81100bd2803b3148a1e59b3dfae6a Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:42 +0200 Subject: [PATCH 12/40] spi: spi-fsl-dspi: Enable extended SPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the XSPI bit for devices configured for XSPI mode (currently LS1021A), and thereby switch to extended SPI mode, allowing for SPI transfers using from 4 to 32 bits per word instead of 4 to 16 bits per word. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index eed55491b2c9..1f85dcdb2203 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -46,6 +46,7 @@ #define SPI_MCR_PCSIS (0x3F << 16) #define SPI_MCR_CLR_TXF (1 << 11) #define SPI_MCR_CLR_RXF (1 << 10) +#define SPI_MCR_XSPI (1 << 3) #define SPI_TCR 0x08 #define SPI_TCR_GET_TCNT(x) (((x) & 0xffff0000) >> 16) @@ -968,7 +969,8 @@ static const struct regmap_config dspi_xspi_regmap_config[] = { static void dspi_init(struct fsl_dspi *dspi) { - regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS); + regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS | + (dspi->devtype_data->xspi_mode ? SPI_MCR_XSPI : 0)); regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR); if (dspi->devtype_data->xspi_mode) regmap_write(dspi->regmap, SPI_CTARE(0), From 80dc12cdfb9e8966da4f02d6d7accf2cb691caf2 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 21 Jun 2018 08:22:09 -0500 Subject: [PATCH 13/40] spi: spi-fsl-dspi: Fix copy-paste error in dspi_probe It seems that the proper structure field to use in this particular case is *regmap_pushr* instead of regmap. Addresses-Coverity-ID: 1470126 ("Copy-paste error") Fixes: 58ba07ec79e6 ("spi: spi-fsl-dspi: Add support for XSPI mode registers") Signed-off-by: Gustavo A. R. Silva Acked-by: Esben Haabendal Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 1f85dcdb2203..ff7456be9d6d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1066,7 +1066,7 @@ static int dspi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to init pushr regmap: %ld\n", PTR_ERR(dspi->regmap_pushr)); - ret = PTR_ERR(dspi->regmap); + ret = PTR_ERR(dspi->regmap_pushr); goto out_master_put; } } From 22d71a5097ec7059b6cbbee678a4f88484695941 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 28 Jun 2018 13:52:23 +0300 Subject: [PATCH 14/40] spi: pxa2xx: Add support for Intel Ice Lake Intel Ice Lake SPI host controller follows the Intel Cannon Lake but the PCI IDs are different. Add the new PCI IDs to the driver supported devices list. Signed-off-by: Mika Westerberg Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/spi/spi-pxa2xx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 0b2d60d30f69..14f4ea59caff 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1391,6 +1391,10 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { { PCI_VDEVICE(INTEL, 0x31c2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x31c4), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x31c6), LPSS_BXT_SSP }, + /* ICL-LP */ + { PCI_VDEVICE(INTEL, 0x34aa), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x34ab), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x34fb), LPSS_CNL_SSP }, /* APL */ { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, From d8ffee2f551a627ffb7b216e2da322cb9a037f77 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 29 Jun 2018 13:33:09 +0200 Subject: [PATCH 15/40] spi: spi-fsl-dspi: Fix imprecise abort on VF500 during probe Registers of DSPI should not be accessed before enabling its clock. On Toradex Colibri VF50 on Iris carrier board this could be seen during bootup as imprecise abort: Unhandled fault: imprecise external abort (0x1c06) at 0x00000000 Internal error: : 1c06 [#1] ARM Modules linked in: CPU: 0 PID: 1 Comm: swapper Not tainted 4.14.39-dirty #97 Hardware name: Freescale Vybrid VF5xx/VF6xx (Device Tree) Backtrace: [<804166a8>] (regmap_write) from [<80466b5c>] (dspi_probe+0x1f0/0x8dc) [<8046696c>] (dspi_probe) from [<8040107c>] (platform_drv_probe+0x54/0xb8) [<80401028>] (platform_drv_probe) from [<803ff53c>] (driver_probe_device+0x280/0x2f8) [<803ff2bc>] (driver_probe_device) from [<803ff674>] (__driver_attach+0xc0/0xc4) [<803ff5b4>] (__driver_attach) from [<803fd818>] (bus_for_each_dev+0x70/0xa4) [<803fd7a8>] (bus_for_each_dev) from [<803fee74>] (driver_attach+0x24/0x28) [<803fee50>] (driver_attach) from [<803fe980>] (bus_add_driver+0x1a0/0x218) [<803fe7e0>] (bus_add_driver) from [<803fffe8>] (driver_register+0x80/0x100) [<803fff68>] (driver_register) from [<80400fdc>] (__platform_driver_register+0x48/0x50) [<80400f94>] (__platform_driver_register) from [<8091cf7c>] (fsl_dspi_driver_init+0x1c/0x20) [<8091cf60>] (fsl_dspi_driver_init) from [<8010195c>] (do_one_initcall+0x4c/0x174) [<80101910>] (do_one_initcall) from [<80900e8c>] (kernel_init_freeable+0x144/0x1d8) [<80900d48>] (kernel_init_freeable) from [<805ff6a8>] (kernel_init+0x10/0x114) [<805ff698>] (kernel_init) from [<80107be8>] (ret_from_fork+0x14/0x2c) Cc: Fixes: 5ee67b587a2b ("spi: dspi: clear SPI_SR before enable interrupt") Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index ff7456be9d6d..89a1e7a4fe5d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1071,21 +1071,6 @@ static int dspi_probe(struct platform_device *pdev) } } - dspi_init(dspi); - dspi->irq = platform_get_irq(pdev, 0); - if (dspi->irq < 0) { - dev_err(&pdev->dev, "can't get platform irq\n"); - ret = dspi->irq; - goto out_master_put; - } - - ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0, - pdev->name, dspi); - if (ret < 0) { - dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n"); - goto out_master_put; - } - dspi->clk = devm_clk_get(&pdev->dev, "dspi"); if (IS_ERR(dspi->clk)) { ret = PTR_ERR(dspi->clk); @@ -1096,6 +1081,21 @@ static int dspi_probe(struct platform_device *pdev) if (ret) goto out_master_put; + dspi_init(dspi); + dspi->irq = platform_get_irq(pdev, 0); + if (dspi->irq < 0) { + dev_err(&pdev->dev, "can't get platform irq\n"); + ret = dspi->irq; + goto out_clk_put; + } + + ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0, + pdev->name, dspi); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n"); + goto out_clk_put; + } + if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) { ret = dspi_request_dma(dspi, res->start); if (ret < 0) { From 504f2565a4f64a3cdd0a4e175a34ffc73297294a Mon Sep 17 00:00:00 2001 From: Liang Chen Date: Tue, 17 Jul 2018 15:49:03 +0800 Subject: [PATCH 16/40] spi: rockchip: add description for px30 Add "rockchip,px30-spi", "rockchip,rk3066-spi" for spi on px30 platform. Signed-off-by: Liang Chen Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-rockchip.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-rockchip.txt b/Documentation/devicetree/bindings/spi/spi-rockchip.txt index 6e3ffacbba32..a0edac12d8df 100644 --- a/Documentation/devicetree/bindings/spi/spi-rockchip.txt +++ b/Documentation/devicetree/bindings/spi/spi-rockchip.txt @@ -7,6 +7,7 @@ Required Properties: - compatible: should be one of the following. "rockchip,rv1108-spi" for rv1108 SoCs. + "rockchip,px30-spi", "rockchip,rk3066-spi" for px30 SoCs. "rockchip,rk3036-spi" for rk3036 SoCS. "rockchip,rk3066-spi" for rk3066 SoCs. "rockchip,rk3188-spi" for rk3188 SoCs. From 5f8f80356eccfaae1db8a827626ae4fc2eb62831 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 16 Jul 2018 21:33:29 -0700 Subject: [PATCH 17/40] spi: spi-fsl-dspi: Fill actual_length when doing DMA transfer Upper layer users of SPI device drivers may rely on 'actual_length', so it is important that information is correctly reported. One such example is spi_mem_exec_op() function that will fail if 'actual_length' of the data transferred is not what was requested. Add necessary code to populate 'actual_length. Cc: Mark Brown Cc: Sanchayan Maity Cc: Stefan Agner Cc: cphealy@gmail.com Cc: linux-spi@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Andrey Smirnov Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 89a1e7a4fe5d..9e598642ca66 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -358,6 +358,7 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) { struct fsl_dspi_dma *dma = dspi->dma; struct device *dev = &dspi->pdev->dev; + struct spi_message *message = dspi->cur_msg; int curr_remaining_bytes; int bytes_per_buffer; int ret = 0; @@ -377,8 +378,10 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) goto exit; } else { - curr_remaining_bytes -= dma->curr_xfer_len - * dspi->bytes_per_word; + const int len = + dma->curr_xfer_len * dspi->bytes_per_word; + curr_remaining_bytes -= len; + message->actual_length += len; if (curr_remaining_bytes < 0) curr_remaining_bytes = 0; } From 62dbbae483b6452e688b8e26c4d024d484117697 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 17 Jul 2018 16:23:11 +0200 Subject: [PATCH 18/40] spi: dw: allow providing own set_cs callback Allow platform specific drivers to provide their own set_cs callback when the IP integration requires it. Signed-off-by: Alexandre Belloni Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 3 +++ drivers/spi/spi-dw.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index f693bfe95ab9..0d8ccb8be5ec 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -505,6 +505,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->dev.of_node = dev->of_node; master->flags = SPI_MASTER_GPIO_SS; + if (dws->set_cs) + master->set_cs = dws->set_cs; + /* Basic HW init */ spi_hw_init(dev, dws); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 2cde2473b3e9..446013022849 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -112,6 +112,7 @@ struct dw_spi { u32 reg_io_width; /* DR I/O width in bytes */ u16 bus_num; u16 num_cs; /* supported slave numbers */ + void (*set_cs)(struct spi_device *spi, bool enable); /* Current message transfer state info */ size_t len; From 7c201ead1bfd19935f689fca69100c335028c047 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 16 Jul 2018 22:20:49 -0500 Subject: [PATCH 19/40] spi: spi-bitbang: change flags from u8 to u16 This changes the data type of the flags field in struct spi_bitbang from u8 to u16. This matches the size of the mode field of struct spi_device where these flags are also used. This is done in preparation of adding a new SPI mode flag that will be used with this field that would otherwise not fit in 8 bits. Signed-off-by: David Lechner Signed-off-by: Mark Brown --- include/linux/spi/spi_bitbang.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index 51d8c060e513..c1e61641a7f1 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -8,7 +8,7 @@ struct spi_bitbang { struct mutex lock; u8 busy; u8 use_dma; - u8 flags; /* extra spi->mode support */ + u16 flags; /* extra spi->mode support */ struct spi_master *master; From 6576bf009aab00f8376efeb24628dd2b86f68200 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 24 Jul 2018 10:04:02 -0300 Subject: [PATCH 20/40] spi: spi-fsl-dspi: Switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 9e598642ca66..7cb3ab0a35a0 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1,17 +1,9 @@ -/* - * drivers/spi/spi-fsl-dspi.c - * - * Copyright 2013 Freescale Semiconductor, Inc. - * - * Freescale DSPI driver - * This file contains a driver for the Freescale DSPI - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2013 Freescale Semiconductor, Inc. +// +// Freescale DSPI driver +// This file contains a driver for the Freescale DSPI #include #include From a021cac420f8a3bd1c36c91af9dd3143c44101d7 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:50 +0200 Subject: [PATCH 21/40] spi: imx: Remove duplicate variable assignments Some fields in struct spi_imx_data are assigned a different value twice in a row, in spi_imx_setupxfer. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index d3b21faf6b1f..d75153c995af 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1106,8 +1106,6 @@ static int spi_imx_setupxfer(struct spi_device *spi, if (spi_imx->devtype_data->dynamic_burst && !spi_imx->slave_mode) { u32 mask; - spi_imx->dynamic_burst = 0; - spi_imx->remainder = 0; spi_imx->read_u32 = 1; mask = (1 << spi_imx->bits_per_word) - 1; From 2801b2f5fad3d1e9ea0ac8484584051071065645 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:51 +0200 Subject: [PATCH 22/40] spi: imx: Use dynamic bursts only when bits_per_word is 8, 16 or 32 The dynamic bursts mode allows to group together multiple words into a single burst. To do so, it's necessary that words can be packed into the 32-bits FIFO entries, so we can't allow using this mode with bit_per_words different to 8, 16 or 32. This prevents shitfing out extra clock ticks for transfers with bit_per_word values not aligned on 8 bits. With that , we are sure that only the correct number of bits is shifted out at each transfer, so we don't need to mask out the remaining parts of the words. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index d75153c995af..ecafbda5ec94 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -95,7 +95,6 @@ struct spi_imx_data { const void *tx_buf; unsigned int txfifo; /* number of words pushed in tx FIFO */ unsigned int dynamic_burst, read_u32; - unsigned int word_mask; /* Slave mode */ bool slave_mode; @@ -291,7 +290,6 @@ static void spi_imx_buf_rx_swap_u32(struct spi_imx_data *spi_imx) else if (bytes_per_word == 2) val = (val << 16) | (val >> 16); #endif - val &= spi_imx->word_mask; *(u32 *)spi_imx->rx_buf = val; spi_imx->rx_buf += sizeof(u32); } @@ -322,7 +320,6 @@ static void spi_imx_buf_tx_swap_u32(struct spi_imx_data *spi_imx) if (spi_imx->tx_buf) { val = *(u32 *)spi_imx->tx_buf; - val &= spi_imx->word_mask; spi_imx->tx_buf += sizeof(u32); } @@ -1102,25 +1099,23 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->bits_per_word = t->bits_per_word; spi_imx->speed_hz = t->speed_hz; - /* Initialize the functions for transfer */ - if (spi_imx->devtype_data->dynamic_burst && !spi_imx->slave_mode) { - u32 mask; + /* + * Initialize the functions for transfer. To transfer non byte-aligned + * words, we have to use multiple word-size bursts, we can't use + * dynamic_burst in that case. + */ + if (spi_imx->devtype_data->dynamic_burst && !spi_imx->slave_mode && + (spi_imx->bits_per_word == 8 || + spi_imx->bits_per_word == 16 || + spi_imx->bits_per_word == 32)) { spi_imx->read_u32 = 1; - mask = (1 << spi_imx->bits_per_word) - 1; spi_imx->rx = spi_imx_buf_rx_swap; spi_imx->tx = spi_imx_buf_tx_swap; spi_imx->dynamic_burst = 1; spi_imx->remainder = t->len; - if (spi_imx->bits_per_word <= 8) - spi_imx->word_mask = mask << 24 | mask << 16 - | mask << 8 | mask; - else if (spi_imx->bits_per_word <= 16) - spi_imx->word_mask = mask << 16 | mask; - else - spi_imx->word_mask = mask; } else { if (spi_imx->bits_per_word <= 8) { spi_imx->rx = spi_imx_buf_rx_u8; From afb27208146af82b249e0cdc40142b4ffd211887 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:52 +0200 Subject: [PATCH 23/40] spi: imx: Use correct number of bytes per words The SPI core enforces that we always use the next power-of-two number of bytes to store words. As a result, a 24 bits word will be stored in 4 bytes. This commit fixes the spi_imx_bytes_per_word function to return the correct number of bytes. This also allows to get rid of unnecessary checks in the can_dma function, since the SPI core validates that we always have a transfer length that is a multiple of the number of bytes per word. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index ecafbda5ec94..3ae706dac660 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -202,7 +202,12 @@ out: static int spi_imx_bytes_per_word(const int bits_per_word) { - return DIV_ROUND_UP(bits_per_word, BITS_PER_BYTE); + if (bits_per_word <= 8) + return 1; + else if (bits_per_word <= 16) + return 2; + else + return 4; } static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, @@ -219,9 +224,6 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, bytes_per_word = spi_imx_bytes_per_word(transfer->bits_per_word); - if (bytes_per_word != 1 && bytes_per_word != 2 && bytes_per_word != 4) - return false; - for (i = spi_imx->devtype_data->fifo_size / 2; i > 0; i--) { if (!(transfer->len % (i * bytes_per_word))) break; From 0486dd4d6111b969a81b9ce63bfaf22e414839a4 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:53 +0200 Subject: [PATCH 24/40] spi: imx: remove unnecessary check in spi_imx_can_dma The spi_imx_can_dma function computes the watermark level so that the transfer will fit in exactly N bursts (without a remainder). The smallest watermark level possible being one FIFO entry per burst, we can't never have a case where the transfer size isn't divsiible by 1. Remove the extra check for the wml being different than 0. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 3ae706dac660..ef6d3648396a 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -229,9 +229,6 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, break; } - if (i == 0) - return false; - spi_imx->wml = i; spi_imx->dynamic_burst = 0; From 2ca300ac6e7b5143e0c03cfc91bb064dcad376f4 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:54 +0200 Subject: [PATCH 25/40] spi: imx: Use the longuest possible burst size when in dynamic_burst Dynamic burst mode allows to group together multiple words and send them in one continuous burst. When the number of bytes to be sent is not a strict multiple of the FIFO entry size (32 bits), the controller expects the non aligned bits to be sent first. This commit adds support for this particular constraint, avoiding the need to send the non-aligned bytes one by one at the end of the transfer, speeding-up transfer speed in that case. With this method, a transfer is divided into multiple bursts, limited in size by the maximum amount of data that the controller can transfer in one continuous burst (which is 512 bytes). The non-512 byte part of the transfer is sent first. The remaining bytes to be transferred in the current burst is stored in the 'remainder' field. With this method, the read_u32 field is no longer necessary, and is removed. This was tested on imx6 solo and imx6 quad. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 124 +++++++++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 38 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index ef6d3648396a..08dd3a31a3e5 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -94,7 +94,7 @@ struct spi_imx_data { void *rx_buf; const void *tx_buf; unsigned int txfifo; /* number of words pushed in tx FIFO */ - unsigned int dynamic_burst, read_u32; + unsigned int dynamic_burst; /* Slave mode */ bool slave_mode; @@ -139,6 +139,8 @@ static void spi_imx_buf_rx_##type(struct spi_imx_data *spi_imx) \ *(type *)spi_imx->rx_buf = val; \ spi_imx->rx_buf += sizeof(type); \ } \ + \ + spi_imx->remainder -= sizeof(type); \ } #define MXC_SPI_BUF_TX(type) \ @@ -292,22 +294,36 @@ static void spi_imx_buf_rx_swap_u32(struct spi_imx_data *spi_imx) *(u32 *)spi_imx->rx_buf = val; spi_imx->rx_buf += sizeof(u32); } + + spi_imx->remainder -= sizeof(u32); } static void spi_imx_buf_rx_swap(struct spi_imx_data *spi_imx) { - unsigned int bytes_per_word; + int unaligned; + u32 val; - bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word); - if (spi_imx->read_u32) { + unaligned = spi_imx->remainder % 4; + + if (!unaligned) { spi_imx_buf_rx_swap_u32(spi_imx); return; } - if (bytes_per_word == 1) - spi_imx_buf_rx_u8(spi_imx); - else if (bytes_per_word == 2) + if (spi_imx_bytes_per_word(spi_imx->bits_per_word) == 2) { spi_imx_buf_rx_u16(spi_imx); + return; + } + + val = readl(spi_imx->base + MXC_CSPIRXDATA); + + while (unaligned--) { + if (spi_imx->rx_buf) { + *(u8 *)spi_imx->rx_buf = (val >> (8 * unaligned)) & 0xff; + spi_imx->rx_buf++; + } + spi_imx->remainder--; + } } static void spi_imx_buf_tx_swap_u32(struct spi_imx_data *spi_imx) @@ -336,40 +352,30 @@ static void spi_imx_buf_tx_swap_u32(struct spi_imx_data *spi_imx) static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx) { - u32 ctrl, val; - unsigned int bytes_per_word; + int unaligned; + u32 val = 0; - if (spi_imx->count == spi_imx->remainder) { - ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL); - ctrl &= ~MX51_ECSPI_CTRL_BL_MASK; - if (spi_imx->count > MX51_ECSPI_CTRL_MAX_BURST) { - spi_imx->remainder = spi_imx->count % - MX51_ECSPI_CTRL_MAX_BURST; - val = MX51_ECSPI_CTRL_MAX_BURST * 8 - 1; - } else if (spi_imx->count >= sizeof(u32)) { - spi_imx->remainder = spi_imx->count % sizeof(u32); - val = (spi_imx->count - spi_imx->remainder) * 8 - 1; - } else { - spi_imx->remainder = 0; - val = spi_imx->bits_per_word - 1; - spi_imx->read_u32 = 0; - } + unaligned = spi_imx->count % 4; - ctrl |= (val << MX51_ECSPI_CTRL_BL_OFFSET); - writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); - } - - if (spi_imx->count >= sizeof(u32)) { + if (!unaligned) { spi_imx_buf_tx_swap_u32(spi_imx); return; } - bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word); - - if (bytes_per_word == 1) - spi_imx_buf_tx_u8(spi_imx); - else if (bytes_per_word == 2) + if (spi_imx_bytes_per_word(spi_imx->bits_per_word) == 2) { spi_imx_buf_tx_u16(spi_imx); + return; + } + + while (unaligned--) { + if (spi_imx->tx_buf) { + val |= *(u8 *)spi_imx->tx_buf << (8 * unaligned); + spi_imx->tx_buf++; + } + spi_imx->count--; + } + + writel(val, spi_imx->base + MXC_CSPITXDATA); } static void mx53_ecspi_rx_slave(struct spi_imx_data *spi_imx) @@ -388,6 +394,8 @@ static void mx53_ecspi_rx_slave(struct spi_imx_data *spi_imx) spi_imx->rx_buf += n_bytes; spi_imx->slave_burst -= n_bytes; } + + spi_imx->remainder -= sizeof(u32); } static void mx53_ecspi_tx_slave(struct spi_imx_data *spi_imx) @@ -997,12 +1005,52 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active) gpio_set_value(spi->cs_gpio, dev_is_lowactive ^ active); } +static void spi_imx_set_burst_len(struct spi_imx_data *spi_imx, int n_bits) +{ + u32 ctrl; + + ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL); + ctrl &= ~MX51_ECSPI_CTRL_BL_MASK; + ctrl |= ((n_bits - 1) << MX51_ECSPI_CTRL_BL_OFFSET); + writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); +} + static void spi_imx_push(struct spi_imx_data *spi_imx) { + unsigned int burst_len, fifo_words; + + if (spi_imx->dynamic_burst) + fifo_words = 4; + else + fifo_words = spi_imx_bytes_per_word(spi_imx->bits_per_word); + /* + * Reload the FIFO when the remaining bytes to be transferred in the + * current burst is 0. This only applies when bits_per_word is a + * multiple of 8. + */ + if (!spi_imx->remainder) { + if (spi_imx->dynamic_burst) { + + /* We need to deal unaligned data first */ + burst_len = spi_imx->count % MX51_ECSPI_CTRL_MAX_BURST; + + if (!burst_len) + burst_len = MX51_ECSPI_CTRL_MAX_BURST; + + spi_imx_set_burst_len(spi_imx, burst_len * 8); + + spi_imx->remainder = burst_len; + } else { + spi_imx->remainder = fifo_words; + } + } + while (spi_imx->txfifo < spi_imx->devtype_data->fifo_size) { if (!spi_imx->count) break; - if (spi_imx->txfifo && (spi_imx->count == spi_imx->remainder)) + if (spi_imx->dynamic_burst && + spi_imx->txfifo >= DIV_ROUND_UP(spi_imx->remainder, + fifo_words)) break; spi_imx->tx(spi_imx); spi_imx->txfifo++; @@ -1108,12 +1156,9 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->bits_per_word == 16 || spi_imx->bits_per_word == 32)) { - spi_imx->read_u32 = 1; - spi_imx->rx = spi_imx_buf_rx_swap; spi_imx->tx = spi_imx_buf_tx_swap; spi_imx->dynamic_burst = 1; - spi_imx->remainder = t->len; } else { if (spi_imx->bits_per_word <= 8) { @@ -1126,6 +1171,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->rx = spi_imx_buf_rx_u32; spi_imx->tx = spi_imx_buf_tx_u32; } + spi_imx->dynamic_burst = 0; } if (spi_imx_can_dma(spi_imx->bitbang.master, spi, t)) @@ -1309,6 +1355,7 @@ static int spi_imx_pio_transfer(struct spi_device *spi, spi_imx->rx_buf = transfer->rx_buf; spi_imx->count = transfer->len; spi_imx->txfifo = 0; + spi_imx->remainder = 0; reinit_completion(&spi_imx->xfer_done); @@ -1346,6 +1393,7 @@ static int spi_imx_pio_transfer_slave(struct spi_device *spi, spi_imx->rx_buf = transfer->rx_buf; spi_imx->count = transfer->len; spi_imx->txfifo = 0; + spi_imx->remainder = 0; reinit_completion(&spi_imx->xfer_done); spi_imx->slave_aborted = false; From 516ddd79068dc02b9101bf9551a9ef7dce217b5b Mon Sep 17 00:00:00 2001 From: Tiago Brusamarello Date: Thu, 26 Jul 2018 11:12:11 -0300 Subject: [PATCH 26/40] spi: spi-fsl-espi: Log fifo counters on error Log RX and TX fifo counters when a transfer is done and these are not zero. Signed-off-by: Tiago Brusamarello Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 1d332e23f6ed..1e8ff6256079 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -547,8 +547,11 @@ static void fsl_espi_cpu_irq(struct fsl_espi *espi, u32 events) dev_err(espi->dev, "Transfer done but SPIE_DON isn't set!\n"); - if (SPIE_RXCNT(events) || SPIE_TXCNT(events) != FSL_ESPI_FIFO_SIZE) + if (SPIE_RXCNT(events) || SPIE_TXCNT(events) != FSL_ESPI_FIFO_SIZE) { dev_err(espi->dev, "Transfer done but rx/tx fifo's aren't empty!\n"); + dev_err(espi->dev, "SPIE_RXCNT = %d, SPIE_TXCNT = %d\n", + SPIE_RXCNT(events), SPIE_TXCNT(events)); + } complete(&espi->done); } From c79bdbb490a1d05c6169cbffd37c19f5aa96587a Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 27 Jul 2018 21:53:54 +0200 Subject: [PATCH 27/40] spi: dw: export dw_spi_set_cs Export dw_spi_set_cs so it can be used from the various IP integration modules. Signed-off-by: Alexandre Belloni Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 3 ++- drivers/spi/spi-dw.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 0d8ccb8be5ec..683a4f137a25 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -133,7 +133,7 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws) } #endif /* CONFIG_DEBUG_FS */ -static void dw_spi_set_cs(struct spi_device *spi, bool enable) +void dw_spi_set_cs(struct spi_device *spi, bool enable) { struct dw_spi *dws = spi_controller_get_devdata(spi->controller); struct chip_data *chip = spi_get_ctldata(spi); @@ -145,6 +145,7 @@ static void dw_spi_set_cs(struct spi_device *spi, bool enable) if (!enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); } +EXPORT_SYMBOL_GPL(dw_spi_set_cs); /* Return the max entries we can fill into tx fifo */ static inline u32 tx_max(struct dw_spi *dws) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 446013022849..0168b08364d5 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -245,6 +245,7 @@ struct dw_spi_chip { void (*cs_control)(u32 command); }; +extern void dw_spi_set_cs(struct spi_device *spi, bool enable); extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); From c2c25cc397026ec705e050248539df400d2563f8 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 27 Jul 2018 21:53:56 +0200 Subject: [PATCH 28/40] spi: dw-mmio: add MSCC Ocelot support Because the SPI controller deasserts the chip select when the TX fifo is empty (which may happen in the middle of a transfer), the CS should be handled by linux. Unfortunately, some or all of the first four chip selects are not muxable as GPIOs, depending on the SoC. There is a way to bitbang those pins by using the SPI boot controller so use it to set the chip selects. At init time, it is also necessary to give control of the SPI interface to the Designware IP. Signed-off-by: Alexandre Belloni Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 90 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index d25cc4037e23..e80f60ed6fdf 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -15,11 +15,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "spi-dw.h" @@ -28,10 +30,90 @@ struct dw_spi_mmio { struct dw_spi dws; struct clk *clk; + void *priv; }; +#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 +#define OCELOT_IF_SI_OWNER_MASK GENMASK(5, 4) +#define OCELOT_IF_SI_OWNER_OFFSET 4 +#define MSCC_IF_SI_OWNER_SISL 0 +#define MSCC_IF_SI_OWNER_SIBM 1 +#define MSCC_IF_SI_OWNER_SIMC 2 + +#define MSCC_SPI_MST_SW_MODE 0x14 +#define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE BIT(13) +#define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5) + +struct dw_spi_mscc { + struct regmap *syscon; + void __iomem *spi_mst; +}; + +/* + * The Designware SPI controller (referred to as master in the documentation) + * automatically deasserts chip select when the tx fifo is empty. The chip + * selects then needs to be either driven as GPIOs or, for the first 4 using the + * the SPI boot controller registers. the final chip select is an OR gate + * between the Designware SPI controller and the SPI boot controller. + */ +static void dw_spi_mscc_set_cs(struct spi_device *spi, bool enable) +{ + struct dw_spi *dws = spi_master_get_devdata(spi->master); + struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws); + struct dw_spi_mscc *dwsmscc = dwsmmio->priv; + u32 cs = spi->chip_select; + + if (cs < 4) { + u32 sw_mode = MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE; + + if (!enable) + sw_mode |= MSCC_SPI_MST_SW_MODE_SW_SPI_CS(BIT(cs)); + + writel(sw_mode, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE); + } + + dw_spi_set_cs(spi, enable); +} + +static int dw_spi_mscc_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + struct dw_spi_mscc *dwsmscc; + struct resource *res; + + dwsmscc = devm_kzalloc(&pdev->dev, sizeof(*dwsmscc), GFP_KERNEL); + if (!dwsmscc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dwsmscc->spi_mst = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dwsmscc->spi_mst)) { + dev_err(&pdev->dev, "SPI_MST region map failed\n"); + return PTR_ERR(dwsmscc->spi_mst); + } + + dwsmscc->syscon = syscon_regmap_lookup_by_compatible("mscc,ocelot-cpu-syscon"); + if (IS_ERR(dwsmscc->syscon)) + return PTR_ERR(dwsmscc->syscon); + + /* Deassert all CS */ + writel(0, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE); + + /* Select the owner of the SI interface */ + regmap_update_bits(dwsmscc->syscon, MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL, + OCELOT_IF_SI_OWNER_MASK, + MSCC_IF_SI_OWNER_SIMC << OCELOT_IF_SI_OWNER_OFFSET); + + dwsmmio->dws.set_cs = dw_spi_mscc_set_cs; + dwsmmio->priv = dwsmscc; + + return 0; +} + static int dw_spi_mmio_probe(struct platform_device *pdev) { + int (*init_func)(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio); struct dw_spi_mmio *dwsmmio; struct dw_spi *dws; struct resource *mem; @@ -99,6 +181,13 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) } } + init_func = device_get_match_data(&pdev->dev); + if (init_func) { + ret = init_func(pdev, dwsmmio); + if (ret) + goto out; + } + ret = dw_spi_add_host(&pdev->dev, dws); if (ret) goto out; @@ -123,6 +212,7 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "snps,dw-apb-ssi", }, + { .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); From 45e41bc5259242921da755c60c705ff368beb8ce Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 30 Jul 2018 15:45:46 +0100 Subject: [PATCH 29/40] spi: omap2-mcspi: remove several redundant variables Variable count, l, mcspi and spi_cntrl are being assigned but are never used hence they are redundant and can be removed. Cleans up clang warnings: warning: variable 'count' set but not used [-Wunused-but-set-variable] warning: variable 'l' set but not used [-Wunused-but-set-variable] warning: variable 'mcspi' set but not used [-Wunused-but-set-variable] warning: variable 'spi_cntrl' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 6c628a54e946..508c61c669e7 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -398,11 +398,9 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, { struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - unsigned int count; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - count = xfer->len; if (mcspi_dma->dma_tx) { struct dma_async_tx_descriptor *tx; @@ -582,7 +580,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi_dma *mcspi_dma; unsigned int count; - u32 l; u8 *rx; const u8 *tx; struct dma_slave_config cfg; @@ -595,8 +592,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - l = mcspi_cached_chconf0(spi); - if (cs->word_len <= 8) { width = DMA_SLAVE_BUSWIDTH_1_BYTE; @@ -676,7 +671,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) static unsigned omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) { - struct omap2_mcspi *mcspi; struct omap2_mcspi_cs *cs = spi->controller_state; unsigned int count, c; u32 l; @@ -686,7 +680,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) void __iomem *chstat_reg; int word_len; - mcspi = spi_master_get_devdata(spi->master); count = xfer->len; c = count; word_len = cs->word_len; @@ -883,13 +876,11 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, { struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; - struct spi_master *spi_cntrl; u32 l = 0, clkd = 0, div, extclk = 0, clkg = 0; u8 word_len = spi->bits_per_word; u32 speed_hz = spi->max_speed_hz; mcspi = spi_master_get_devdata(spi->master); - spi_cntrl = mcspi->master; if (t != NULL && t->bits_per_word) word_len = t->bits_per_word; From baec8eb323cd59d1bfe148a287610819d5897838 Mon Sep 17 00:00:00 2001 From: Ionela Voinescu Date: Sun, 22 Jul 2018 23:20:06 +0200 Subject: [PATCH 30/40] spi: img-spfi: Set device select bits for SPFI port state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even if the chip select line is not controlled by the SPFI hardware, the device select bits need to be set to specify the chip select line in use for the hardware to know what parameters to use for the current transfer. Signed-off-by: Ionela Voinescu Signed-off-by: Andreas Färber Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index 7a37090dabbe..e6eb979f1b8a 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -419,6 +419,9 @@ static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg) u32 val; val = spfi_readl(spfi, SPFI_PORT_STATE); + val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK << + SPFI_PORT_STATE_DEV_SEL_SHIFT); + val |= msg->spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT; if (msg->spi->mode & SPI_CPHA) val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select); else From f09757ab401ff332030f8e3a41cec6a44e6d9461 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 31 Jul 2018 16:38:53 +0200 Subject: [PATCH 31/40] spi: dw: document Microsemi integration The integration of the Designware SPI controller on Microsemi SoCs requires an extra register set to be able to give the IP control of the SPI interface. Cc: Rob Herring Signed-off-by: Alexandre Belloni Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt index 204b311e0400..642d3fb1ef85 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt @@ -1,8 +1,10 @@ Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface. Required properties: -- compatible : "snps,dw-apb-ssi" -- reg : The register base for the controller. +- compatible : "snps,dw-apb-ssi" or "mscc,-spi", where soc is "ocelot" or + "jaguar2" +- reg : The register base for the controller. For "mscc,-spi", a second + register set is required (named ICPU_CFG:SPI_MST) - interrupts : One interrupt, used by the controller. - #address-cells : <1>, as required by generic SPI binding. - #size-cells : <0>, also as required by generic SPI binding. From 4dcd5c2781f3c5c7851800649047658956d590af Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Wed, 1 Aug 2018 16:29:11 +0900 Subject: [PATCH 32/40] spi: add DT bindings for UniPhier SPI controller Add DT bindings for SPI controller implemented in UniPhier SoCs. Signed-off-by: Kunihiko Hayashi Signed-off-by: Keiji Hayashibara Reviewed-by: Rob Herring Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-uniphier.txt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-uniphier.txt diff --git a/Documentation/devicetree/bindings/spi/spi-uniphier.txt b/Documentation/devicetree/bindings/spi/spi-uniphier.txt new file mode 100644 index 000000000000..504a4ecfc7b1 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-uniphier.txt @@ -0,0 +1,22 @@ +Socionext UniPhier SPI controller driver + +UniPhier SoCs have SCSSI which supports SPI single channel. + +Required properties: + - compatible: should be "socionext,uniphier-scssi" + - reg: address and length of the spi master registers + - #address-cells: must be <1>, see spi-bus.txt + - #size-cells: must be <0>, see spi-bus.txt + - clocks: A phandle to the clock for the device. + - resets: A phandle to the reset control for the device. + +Example: + +spi0: spi@54006000 { + compatible = "socionext,uniphier-scssi"; + reg = <0x54006000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&peri_clk 11>; + resets = <&peri_rst 11>; +}; From 5ba155a4d4cc8e4cdd3db6df7d03271a3bd91177 Mon Sep 17 00:00:00 2001 From: Keiji Hayashibara Date: Wed, 1 Aug 2018 16:29:12 +0900 Subject: [PATCH 33/40] spi: add SPI controller driver for UniPhier SoC Add SPI controller driver implemented in Socionext UniPhier SoCs. UniPhier SoCs have two types SPI controllers; SCSSI supports a single channel, and MCSSI supports multiple channels. This driver supports SCSSI only. This controller has 32bit TX/RX FIFO with depth of eight entry, and supports the SPI master mode only. This commit is implemented in PIO transfer mode, not DMA transfer. Signed-off-by: Kunihiko Hayashi Signed-off-by: Keiji Hayashibara Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 13 + drivers/spi/Makefile | 1 + drivers/spi/spi-uniphier.c | 525 +++++++++++++++++++++++++++++++++++++ 3 files changed, 539 insertions(+) create mode 100644 drivers/spi/spi-uniphier.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ad5d68e1dab7..671d078349cc 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -688,6 +688,19 @@ config SPI_TXX9 help SPI driver for Toshiba TXx9 MIPS SoCs +config SPI_UNIPHIER + tristate "Socionext UniPhier SPI Controller" + depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF + help + This enables a driver for the Socionext UniPhier SoC SCSSI SPI controller. + + UniPhier SoCs have SCSSI and MCSSI SPI controllers. + Every UniPhier SoC has SCSSI which supports single channel. + Older UniPhier Pro4/Pro5 also has MCSSI which support multiple channels. + This driver supports SCSSI only. + + If your SoC supports SCSSI, say Y here. + config SPI_XCOMM tristate "Analog Devices AD-FMCOMMS1-EBZ SPI-I2C-bridge driver" depends on I2C diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index cb1f4378b87c..a90d55970036 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -101,6 +101,7 @@ spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o +obj-$(CONFIG_SPI_UNIPHIER) += spi-uniphier.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o obj-$(CONFIG_SPI_XLP) += spi-xlp.o diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c new file mode 100644 index 000000000000..089985465890 --- /dev/null +++ b/drivers/spi/spi-uniphier.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0 +// spi-uniphier.c - Socionext UniPhier SPI controller driver +// Copyright 2012 Panasonic Corporation +// Copyright 2016-2018 Socionext Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SSI_TIMEOUT_MS 2000 +#define SSI_MAX_CLK_DIVIDER 254 +#define SSI_MIN_CLK_DIVIDER 4 + +struct uniphier_spi_priv { + void __iomem *base; + struct clk *clk; + struct spi_master *master; + struct completion xfer_done; + + int error; + unsigned int tx_bytes; + unsigned int rx_bytes; + const u8 *tx_buf; + u8 *rx_buf; + + bool is_save_param; + u8 bits_per_word; + u16 mode; + u32 speed_hz; +}; + +#define SSI_CTL 0x00 +#define SSI_CTL_EN BIT(0) + +#define SSI_CKS 0x04 +#define SSI_CKS_CKRAT_MASK GENMASK(7, 0) +#define SSI_CKS_CKPHS BIT(14) +#define SSI_CKS_CKINIT BIT(13) +#define SSI_CKS_CKDLY BIT(12) + +#define SSI_TXWDS 0x08 +#define SSI_TXWDS_WDLEN_MASK GENMASK(13, 8) +#define SSI_TXWDS_TDTF_MASK GENMASK(7, 6) +#define SSI_TXWDS_DTLEN_MASK GENMASK(5, 0) + +#define SSI_RXWDS 0x0c +#define SSI_RXWDS_DTLEN_MASK GENMASK(5, 0) + +#define SSI_FPS 0x10 +#define SSI_FPS_FSPOL BIT(15) +#define SSI_FPS_FSTRT BIT(14) + +#define SSI_SR 0x14 +#define SSI_SR_RNE BIT(0) + +#define SSI_IE 0x18 +#define SSI_IE_RCIE BIT(3) +#define SSI_IE_RORIE BIT(0) + +#define SSI_IS 0x1c +#define SSI_IS_RXRS BIT(9) +#define SSI_IS_RCID BIT(3) +#define SSI_IS_RORID BIT(0) + +#define SSI_IC 0x1c +#define SSI_IC_TCIC BIT(4) +#define SSI_IC_RCIC BIT(3) +#define SSI_IC_RORIC BIT(0) + +#define SSI_FC 0x20 +#define SSI_FC_TXFFL BIT(12) +#define SSI_FC_TXFTH_MASK GENMASK(11, 8) +#define SSI_FC_RXFFL BIT(4) +#define SSI_FC_RXFTH_MASK GENMASK(3, 0) + +#define SSI_TXDR 0x24 +#define SSI_RXDR 0x24 + +#define SSI_FIFO_DEPTH 8U + +static inline unsigned int bytes_per_word(unsigned int bits) +{ + return bits <= 8 ? 1 : (bits <= 16 ? 2 : 4); +} + +static inline void uniphier_spi_irq_enable(struct spi_device *spi, u32 mask) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + val = readl(priv->base + SSI_IE); + val |= mask; + writel(val, priv->base + SSI_IE); +} + +static inline void uniphier_spi_irq_disable(struct spi_device *spi, u32 mask) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + val = readl(priv->base + SSI_IE); + val &= ~mask; + writel(val, priv->base + SSI_IE); +} + +static void uniphier_spi_set_mode(struct spi_device *spi) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val1, val2; + + /* + * clock setting + * CKPHS capture timing. 0:rising edge, 1:falling edge + * CKINIT clock initial level. 0:low, 1:high + * CKDLY clock delay. 0:no delay, 1:delay depending on FSTRT + * (FSTRT=0: 1 clock, FSTRT=1: 0.5 clock) + * + * frame setting + * FSPOL frame signal porarity. 0: low, 1: high + * FSTRT start frame timing + * 0: rising edge of clock, 1: falling edge of clock + */ + switch (spi->mode & (SPI_CPOL | SPI_CPHA)) { + case SPI_MODE_0: + /* CKPHS=1, CKINIT=0, CKDLY=1, FSTRT=0 */ + val1 = SSI_CKS_CKPHS | SSI_CKS_CKDLY; + val2 = 0; + break; + case SPI_MODE_1: + /* CKPHS=0, CKINIT=0, CKDLY=0, FSTRT=1 */ + val1 = 0; + val2 = SSI_FPS_FSTRT; + break; + case SPI_MODE_2: + /* CKPHS=0, CKINIT=1, CKDLY=1, FSTRT=1 */ + val1 = SSI_CKS_CKINIT | SSI_CKS_CKDLY; + val2 = SSI_FPS_FSTRT; + break; + case SPI_MODE_3: + /* CKPHS=1, CKINIT=1, CKDLY=0, FSTRT=0 */ + val1 = SSI_CKS_CKPHS | SSI_CKS_CKINIT; + val2 = 0; + break; + } + + if (!(spi->mode & SPI_CS_HIGH)) + val2 |= SSI_FPS_FSPOL; + + writel(val1, priv->base + SSI_CKS); + writel(val2, priv->base + SSI_FPS); + + val1 = 0; + if (spi->mode & SPI_LSB_FIRST) + val1 |= FIELD_PREP(SSI_TXWDS_TDTF_MASK, 1); + writel(val1, priv->base + SSI_TXWDS); + writel(val1, priv->base + SSI_RXWDS); +} + +static void uniphier_spi_set_transfer_size(struct spi_device *spi, int size) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + val = readl(priv->base + SSI_TXWDS); + val &= ~(SSI_TXWDS_WDLEN_MASK | SSI_TXWDS_DTLEN_MASK); + val |= FIELD_PREP(SSI_TXWDS_WDLEN_MASK, size); + val |= FIELD_PREP(SSI_TXWDS_DTLEN_MASK, size); + writel(val, priv->base + SSI_TXWDS); + + val = readl(priv->base + SSI_RXWDS); + val &= ~SSI_RXWDS_DTLEN_MASK; + val |= FIELD_PREP(SSI_RXWDS_DTLEN_MASK, size); + writel(val, priv->base + SSI_RXWDS); +} + +static void uniphier_spi_set_baudrate(struct spi_device *spi, + unsigned int speed) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val, ckdiv; + + /* + * the supported rates are even numbers from 4 to 254. (4,6,8...254) + * round up as we look for equal or less speed + */ + ckdiv = DIV_ROUND_UP(clk_get_rate(priv->clk), speed); + ckdiv = round_up(ckdiv, 2); + + val = readl(priv->base + SSI_CKS); + val &= ~SSI_CKS_CKRAT_MASK; + val |= ckdiv & SSI_CKS_CKRAT_MASK; + writel(val, priv->base + SSI_CKS); +} + +static void uniphier_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + priv->error = 0; + priv->tx_buf = t->tx_buf; + priv->rx_buf = t->rx_buf; + priv->tx_bytes = priv->rx_bytes = t->len; + + if (!priv->is_save_param || priv->mode != spi->mode) { + uniphier_spi_set_mode(spi); + priv->mode = spi->mode; + } + + if (!priv->is_save_param || priv->bits_per_word != t->bits_per_word) { + uniphier_spi_set_transfer_size(spi, t->bits_per_word); + priv->bits_per_word = t->bits_per_word; + } + + if (!priv->is_save_param || priv->speed_hz != t->speed_hz) { + uniphier_spi_set_baudrate(spi, t->speed_hz); + priv->speed_hz = t->speed_hz; + } + + if (!priv->is_save_param) + priv->is_save_param = true; + + /* reset FIFOs */ + val = SSI_FC_TXFFL | SSI_FC_RXFFL; + writel(val, priv->base + SSI_FC); +} + +static void uniphier_spi_send(struct uniphier_spi_priv *priv) +{ + int wsize; + u32 val = 0; + + wsize = min(bytes_per_word(priv->bits_per_word), priv->tx_bytes); + priv->tx_bytes -= wsize; + + if (priv->tx_buf) { + switch (wsize) { + case 1: + val = *priv->tx_buf; + break; + case 2: + val = get_unaligned_le16(priv->tx_buf); + break; + case 4: + val = get_unaligned_le32(priv->tx_buf); + break; + } + + priv->tx_buf += wsize; + } + + writel(val, priv->base + SSI_TXDR); +} + +static void uniphier_spi_recv(struct uniphier_spi_priv *priv) +{ + int rsize; + u32 val; + + rsize = min(bytes_per_word(priv->bits_per_word), priv->rx_bytes); + priv->rx_bytes -= rsize; + + val = readl(priv->base + SSI_RXDR); + + if (priv->rx_buf) { + switch (rsize) { + case 1: + *priv->rx_buf = val; + break; + case 2: + put_unaligned_le16(val, priv->rx_buf); + break; + case 4: + put_unaligned_le32(val, priv->rx_buf); + break; + } + + priv->rx_buf += rsize; + } +} + +static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) +{ + unsigned int tx_count; + u32 val; + + tx_count = DIV_ROUND_UP(priv->tx_bytes, + bytes_per_word(priv->bits_per_word)); + tx_count = min(tx_count, SSI_FIFO_DEPTH); + + /* set fifo threshold */ + val = readl(priv->base + SSI_FC); + val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); + val |= FIELD_PREP(SSI_FC_TXFTH_MASK, tx_count); + val |= FIELD_PREP(SSI_FC_RXFTH_MASK, tx_count); + writel(val, priv->base + SSI_FC); + + while (tx_count--) + uniphier_spi_send(priv); +} + +static void uniphier_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + val = readl(priv->base + SSI_FPS); + + if (enable) + val |= SSI_FPS_FSPOL; + else + val &= ~SSI_FPS_FSPOL; + + writel(val, priv->base + SSI_FPS); +} + +static int uniphier_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + int status; + + uniphier_spi_setup_transfer(spi, t); + + reinit_completion(&priv->xfer_done); + + uniphier_spi_fill_tx_fifo(priv); + + uniphier_spi_irq_enable(spi, SSI_IE_RCIE | SSI_IE_RORIE); + + status = wait_for_completion_timeout(&priv->xfer_done, + msecs_to_jiffies(SSI_TIMEOUT_MS)); + + uniphier_spi_irq_disable(spi, SSI_IE_RCIE | SSI_IE_RORIE); + + if (status < 0) + return status; + + return priv->error; +} + +static int uniphier_spi_prepare_transfer_hardware(struct spi_master *master) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + + writel(SSI_CTL_EN, priv->base + SSI_CTL); + + return 0; +} + +static int uniphier_spi_unprepare_transfer_hardware(struct spi_master *master) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + + writel(0, priv->base + SSI_CTL); + + return 0; +} + +static irqreturn_t uniphier_spi_handler(int irq, void *dev_id) +{ + struct uniphier_spi_priv *priv = dev_id; + u32 val, stat; + + stat = readl(priv->base + SSI_IS); + val = SSI_IC_TCIC | SSI_IC_RCIC | SSI_IC_RORIC; + writel(val, priv->base + SSI_IC); + + /* rx fifo overrun */ + if (stat & SSI_IS_RORID) { + priv->error = -EIO; + goto done; + } + + /* rx complete */ + if ((stat & SSI_IS_RCID) && (stat & SSI_IS_RXRS)) { + while ((readl(priv->base + SSI_SR) & SSI_SR_RNE) && + (priv->rx_bytes - priv->tx_bytes) > 0) + uniphier_spi_recv(priv); + + if ((readl(priv->base + SSI_SR) & SSI_SR_RNE) || + (priv->rx_bytes != priv->tx_bytes)) { + priv->error = -EIO; + goto done; + } else if (priv->rx_bytes == 0) + goto done; + + /* next tx transfer */ + uniphier_spi_fill_tx_fifo(priv); + + return IRQ_HANDLED; + } + + return IRQ_NONE; + +done: + complete(&priv->xfer_done); + return IRQ_HANDLED; +} + +static int uniphier_spi_probe(struct platform_device *pdev) +{ + struct uniphier_spi_priv *priv; + struct spi_master *master; + struct resource *res; + unsigned long clk_rate; + int irq; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*priv)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + priv = spi_master_get_devdata(master); + priv->master = master; + priv->is_save_param = false; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + ret = PTR_ERR(priv->base); + goto out_master_put; + } + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(priv->clk); + goto out_master_put; + } + + ret = clk_prepare_enable(priv->clk); + if (ret) + goto out_master_put; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ\n"); + ret = irq; + goto out_disable_clk; + } + + ret = devm_request_irq(&pdev->dev, irq, uniphier_spi_handler, + 0, "uniphier-spi", priv); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto out_disable_clk; + } + + init_completion(&priv->xfer_done); + + clk_rate = clk_get_rate(priv->clk); + + master->max_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MIN_CLK_DIVIDER); + master->min_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MAX_CLK_DIVIDER); + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->dev.of_node = pdev->dev.of_node; + master->bus_num = pdev->id; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); + + master->set_cs = uniphier_spi_set_cs; + master->transfer_one = uniphier_spi_transfer_one; + master->prepare_transfer_hardware + = uniphier_spi_prepare_transfer_hardware; + master->unprepare_transfer_hardware + = uniphier_spi_unprepare_transfer_hardware; + master->num_chipselect = 1; + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) + goto out_disable_clk; + + return 0; + +out_disable_clk: + clk_disable_unprepare(priv->clk); + +out_master_put: + spi_master_put(master); + return ret; +} + +static int uniphier_spi_remove(struct platform_device *pdev) +{ + struct uniphier_spi_priv *priv = platform_get_drvdata(pdev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct of_device_id uniphier_spi_match[] = { + { .compatible = "socionext,uniphier-scssi" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_spi_match); + +static struct platform_driver uniphier_spi_driver = { + .probe = uniphier_spi_probe, + .remove = uniphier_spi_remove, + .driver = { + .name = "uniphier-spi", + .of_match_table = uniphier_spi_match, + }, +}; +module_platform_driver(uniphier_spi_driver); + +MODULE_AUTHOR("Kunihiko Hayashi "); +MODULE_AUTHOR("Keiji Hayashibara "); +MODULE_DESCRIPTION("Socionext UniPhier SPI controller driver"); +MODULE_LICENSE("GPL v2"); From 304d34360b099020a12af2abb7e1ac506f4ba16d Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 28 Jul 2018 10:19:13 +0200 Subject: [PATCH 34/40] spi: add flags parameter to txrx_word function pointers Add the capability to specify the flag parameter used in bitbang_txrx_be_cpha{0,1} through the txrx_word function pointers of spi_bitbang data structure. That feature will be used to add spi-3wire support to the spi-gpio controller Signed-off-by: Lorenzo Bianconi Signed-off-by: Mark Brown --- drivers/spi/spi-ath79.c | 2 +- drivers/spi/spi-bitbang.c | 34 ++++++++++++++++++++------------- drivers/spi/spi-butterfly.c | 4 ++-- drivers/spi/spi-gpio.c | 32 +++++++++++++++---------------- drivers/spi/spi-lm70llp.c | 5 +++-- drivers/spi/spi-sh-sci.c | 20 +++++++++++-------- drivers/spi/spi-xtensa-xtfpga.c | 2 +- include/linux/spi/spi_bitbang.h | 2 +- 8 files changed, 57 insertions(+), 44 deletions(-) diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 0719bd484891..3f6b657394de 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -176,7 +176,7 @@ static void ath79_spi_cleanup(struct spi_device *spi) } static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs, - u32 word, u8 bits) + u32 word, u8 bits, unsigned flags) { struct ath79_spi *sp = ath79_spidev_to_sp(spi); u32 ioc = sp->ioc_base; diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 3aa9e6e3dac8..76f1b534bdd7 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -49,22 +49,26 @@ struct spi_bitbang_cs { unsigned nsecs; /* (clock cycle time)/2 */ u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits); + u32 word, u8 bits, unsigned flags); unsigned (*txrx_bufs)(struct spi_device *, u32 (*txrx_word)( struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), - unsigned, struct spi_transfer *); + u32 word, u8 bits, + unsigned flags), + unsigned, struct spi_transfer *, + unsigned); }; static unsigned bitbang_txrx_8( struct spi_device *spi, u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), + u32 word, u8 bits, + unsigned flags), unsigned ns, - struct spi_transfer *t + struct spi_transfer *t, + unsigned flags ) { unsigned bits = t->bits_per_word; unsigned count = t->len; @@ -76,7 +80,7 @@ static unsigned bitbang_txrx_8( if (tx) word = *tx++; - word = txrx_word(spi, ns, word, bits); + word = txrx_word(spi, ns, word, bits, flags); if (rx) *rx++ = word; count -= 1; @@ -88,9 +92,11 @@ static unsigned bitbang_txrx_16( struct spi_device *spi, u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), + u32 word, u8 bits, + unsigned flags), unsigned ns, - struct spi_transfer *t + struct spi_transfer *t, + unsigned flags ) { unsigned bits = t->bits_per_word; unsigned count = t->len; @@ -102,7 +108,7 @@ static unsigned bitbang_txrx_16( if (tx) word = *tx++; - word = txrx_word(spi, ns, word, bits); + word = txrx_word(spi, ns, word, bits, flags); if (rx) *rx++ = word; count -= 2; @@ -114,9 +120,11 @@ static unsigned bitbang_txrx_32( struct spi_device *spi, u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), + u32 word, u8 bits, + unsigned flags), unsigned ns, - struct spi_transfer *t + struct spi_transfer *t, + unsigned flags ) { unsigned bits = t->bits_per_word; unsigned count = t->len; @@ -128,7 +136,7 @@ static unsigned bitbang_txrx_32( if (tx) word = *tx++; - word = txrx_word(spi, ns, word, bits); + word = txrx_word(spi, ns, word, bits, flags); if (rx) *rx++ = word; count -= 4; @@ -236,7 +244,7 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) struct spi_bitbang_cs *cs = spi->controller_state; unsigned nsecs = cs->nsecs; - return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t); + return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, 0); } /*----------------------------------------------------------------------*/ diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 22a31e4a1a11..1a3510215841 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -144,9 +144,9 @@ static void butterfly_chipselect(struct spi_device *spi, int value) static u32 butterfly_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word, - u8 bits) + u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } /*----------------------------------------------------------------------*/ diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 6ae92d4dca19..be68298cbd9c 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -149,27 +149,27 @@ static inline int getmiso(const struct spi_device *spi) */ static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 1, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); } static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits); + return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); } /* @@ -183,30 +183,30 @@ static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, */ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - unsigned flags = spi->master->flags; + flags = spi->master->flags; return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - unsigned flags = spi->master->flags; + flags = spi->master->flags; return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - unsigned flags = spi->master->flags; + flags = spi->master->flags; return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); } static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - unsigned flags = spi->master->flags; + flags = spi->master->flags; return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); } diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c index 61ee0f4269ae..4549efd792da 100644 --- a/drivers/spi/spi-lm70llp.c +++ b/drivers/spi/spi-lm70llp.c @@ -188,9 +188,10 @@ static void lm70_chipselect(struct spi_device *spi, int value) /* * Our actual bitbanger routine. */ -static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) +static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static void spi_lm70llp_attach(struct parport *p) diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c index a9beeeed812c..393701cfca3c 100644 --- a/drivers/spi/spi-sh-sci.c +++ b/drivers/spi/spi-sh-sci.c @@ -80,27 +80,31 @@ static inline u32 getmiso(struct spi_device *dev) #include "spi-bitbang-txrx.h" static u32 sh_sci_spi_txrx_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static u32 sh_sci_spi_txrx_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); } static u32 sh_sci_spi_txrx_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 1, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); } static u32 sh_sci_spi_txrx_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits); + return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); } static void sh_sci_spi_chipselect(struct spi_device *dev, int value) diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c index be6155cba9de..8ce04f829a80 100644 --- a/drivers/spi/spi-xtensa-xtfpga.c +++ b/drivers/spi/spi-xtensa-xtfpga.c @@ -54,7 +54,7 @@ static inline void xtfpga_spi_wait_busy(struct xtfpga_spi *xspi) } static u32 xtfpga_spi_txrx_word(struct spi_device *spi, unsigned nsecs, - u32 v, u8 bits) + u32 v, u8 bits, unsigned flags) { struct xtfpga_spi *xspi = spi_master_get_devdata(spi->master); diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index c1e61641a7f1..5847f00ef9cf 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -30,7 +30,7 @@ struct spi_bitbang { /* txrx_word[SPI_MODE_*]() just looks like a shift register */ u32 (*txrx_word[4])(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits); + u32 word, u8 bits, unsigned flags); }; /* you can call these default bitbang->master methods from your custom From 4b859db2c60692560afbfef1b030d0ddef57b7ee Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 28 Jul 2018 10:19:14 +0200 Subject: [PATCH 35/40] spi: spi-gpio: add SPI_3WIRE support Add SPI_3WIRE support to spi-gpio controller introducing set_line_direction function pointer in spi_bitbang data structure. Spi-gpio controller has been tested using hts221 temp/rh iio sensor running in 3wire mode and lsm6dsm running in 4wire mode Signed-off-by: Lorenzo Bianconi Signed-off-by: Mark Brown --- drivers/spi/spi-bitbang.c | 16 ++++++++++++++++ drivers/spi/spi-gpio.c | 17 ++++++++++++++++- include/linux/spi/spi_bitbang.h | 1 + 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 76f1b534bdd7..f29176000b8d 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -243,7 +243,23 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) { struct spi_bitbang_cs *cs = spi->controller_state; unsigned nsecs = cs->nsecs; + struct spi_bitbang *bitbang; + bitbang = spi_master_get_devdata(spi->master); + if (bitbang->set_line_direction) { + int err; + + err = bitbang->set_line_direction(spi, !!(t->tx_buf)); + if (err < 0) + return err; + } + + if (spi->mode & SPI_3WIRE) { + unsigned flags; + + flags = t->tx_buf ? SPI_MASTER_NO_RX : SPI_MASTER_NO_TX; + return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, flags); + } return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, 0); } diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index be68298cbd9c..0626e6e3ea0c 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -121,7 +121,10 @@ static inline int getmiso(const struct spi_device *spi) { struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); - return !!gpiod_get_value_cansleep(spi_gpio->miso); + if (spi->mode & SPI_3WIRE) + return !!gpiod_get_value_cansleep(spi_gpio->mosi); + else + return !!gpiod_get_value_cansleep(spi_gpio->miso); } /* @@ -250,6 +253,16 @@ static int spi_gpio_setup(struct spi_device *spi) return status; } +static int spi_gpio_set_direction(struct spi_device *spi, bool output) +{ + struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + + if (output) + return gpiod_direction_output(spi_gpio->mosi, 1); + else + return gpiod_direction_input(spi_gpio->mosi); +} + static void spi_gpio_cleanup(struct spi_device *spi) { spi_bitbang_cleanup(spi); @@ -395,6 +408,7 @@ static int spi_gpio_probe(struct platform_device *pdev) return status; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); + master->mode_bits = SPI_3WIRE | SPI_CPHA | SPI_CPOL; master->flags = master_flags; master->bus_num = pdev->id; /* The master needs to think there is a chipselect even if not connected */ @@ -407,6 +421,7 @@ static int spi_gpio_probe(struct platform_device *pdev) spi_gpio->bitbang.master = master; spi_gpio->bitbang.chipselect = spi_gpio_chipselect; + spi_gpio->bitbang.set_line_direction = spi_gpio_set_direction; if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) { spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0; diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index 5847f00ef9cf..b7e021b274dc 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -31,6 +31,7 @@ struct spi_bitbang { u32 (*txrx_word[4])(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags); + int (*set_line_direction)(struct spi_device *spi, bool output); }; /* you can call these default bitbang->master methods from your custom From 07ebbbe458790d3c75f4a2f3634d8e04f66b389b Mon Sep 17 00:00:00 2001 From: Keiji Hayashibara Date: Thu, 2 Aug 2018 13:42:43 +0900 Subject: [PATCH 36/40] spi: uniphier: remove unnecessary include headers This commit removed include headers of linux/of.h and linux/of_platform.h, because they are not used. Signed-off-by: Keiji Hayashibara Signed-off-by: Mark Brown --- drivers/spi/spi-uniphier.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 089985465890..5a6137fe172d 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -10,8 +10,6 @@ #include #include #include -#include -#include #include #include From 06bcb5168d7d49aa3ed449ff13a6d13c30afc3f0 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 2 Aug 2018 14:53:52 +0200 Subject: [PATCH 37/40] spi: spi-mem: Fix a typo in the documentation of struct spi_mem Fix a typo in the @drvpriv description. Signed-off-by: Frieder Schrempf Acked-by: Boris Brezillon Signed-off-by: Mark Brown --- include/linux/spi/spi-mem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index bb4bd15ae1f6..951a2e949d5f 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -122,7 +122,7 @@ struct spi_mem_op { /** * struct spi_mem - describes a SPI memory device * @spi: the underlying SPI device - * @drvpriv: spi_mem_drviver private data + * @drvpriv: spi_mem_driver private data * * Extra information that describe the SPI memory device and may be needed by * the controller to properly handle this device should be placed here. From 5d27a9c8ea9e967d00b92a76d4bb242bf7692f2b Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 2 Aug 2018 14:53:53 +0200 Subject: [PATCH 38/40] spi: spi-mem: Extend the SPI mem interface to set a custom memory name When porting (Q)SPI controller drivers from the MTD layer to the SPI layer, the naming scheme for the memory devices changes. To be able to keep compatibility with the old drivers naming scheme, a name field is added to struct spi_mem and a hook is added to let controller drivers set a custom name for the memory device. Example for the FSL QSPI driver: Name with the old driver: 21e0000.qspi, or with multiple devices: 21e0000.qspi-0, 21e0000.qspi-1, ... Name with the new driver without spi_mem_get_name: spi4.0 Suggested-by: Boris Brezillon Signed-off-by: Frieder Schrempf Reviewed-by: Boris Brezillon Signed-off-by: Mark Brown --- drivers/spi/spi-mem.c | 28 ++++++++++++++++++++++++++++ include/linux/spi/spi-mem.h | 12 ++++++++++++ 2 files changed, 40 insertions(+) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 990770dfa5cf..e43842c7a31a 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -310,6 +310,24 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) } EXPORT_SYMBOL_GPL(spi_mem_exec_op); +/** + * spi_mem_get_name() - Return the SPI mem device name to be used by the + * upper layer if necessary + * @mem: the SPI memory + * + * This function allows SPI mem users to retrieve the SPI mem device name. + * It is useful if the upper layer needs to expose a custom name for + * compatibility reasons. + * + * Return: a string containing the name of the memory device to be used + * by the SPI mem user + */ +const char *spi_mem_get_name(struct spi_mem *mem) +{ + return mem->name; +} +EXPORT_SYMBOL_GPL(spi_mem_get_name); + /** * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to * match controller limitations @@ -344,6 +362,7 @@ static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) static int spi_mem_probe(struct spi_device *spi) { struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); + struct spi_controller *ctlr = spi->controller; struct spi_mem *mem; mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL); @@ -351,6 +370,15 @@ static int spi_mem_probe(struct spi_device *spi) return -ENOMEM; mem->spi = spi; + + if (ctlr->mem_ops && ctlr->mem_ops->get_name) + mem->name = ctlr->mem_ops->get_name(mem); + else + mem->name = dev_name(&spi->dev); + + if (IS_ERR_OR_NULL(mem->name)) + return PTR_ERR(mem->name); + spi_set_drvdata(spi, mem); return memdrv->probe(mem); diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 951a2e949d5f..0d64ccc4e584 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -123,6 +123,7 @@ struct spi_mem_op { * struct spi_mem - describes a SPI memory device * @spi: the underlying SPI device * @drvpriv: spi_mem_driver private data + * @name: name of the SPI memory device * * Extra information that describe the SPI memory device and may be needed by * the controller to properly handle this device should be placed here. @@ -133,6 +134,7 @@ struct spi_mem_op { struct spi_mem { struct spi_device *spi; void *drvpriv; + char *name; }; /** @@ -165,6 +167,13 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem) * limitations) * @supports_op: check if an operation is supported by the controller * @exec_op: execute a SPI memory operation + * @get_name: get a custom name for the SPI mem device from the controller. + * This might be needed if the controller driver has been ported + * to use the SPI mem layer and a custom name is used to keep + * mtdparts compatible. + * Note that if the implementation of this function allocates memory + * dynamically, then it should do so with devm_xxx(), as we don't + * have a ->free_name() function. * * This interface should be implemented by SPI controllers providing an * high-level interface to execute SPI memory operation, which is usually the @@ -176,6 +185,7 @@ struct spi_controller_mem_ops { const struct spi_mem_op *op); int (*exec_op)(struct spi_mem *mem, const struct spi_mem_op *op); + const char *(*get_name)(struct spi_mem *mem); }; /** @@ -234,6 +244,8 @@ bool spi_mem_supports_op(struct spi_mem *mem, int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op); +const char *spi_mem_get_name(struct spi_mem *mem); + int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, struct module *owner); From b02b17f55b2e789b9747cf4dd2eaaa110439a4cc Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 2 Aug 2018 14:53:54 +0200 Subject: [PATCH 39/40] mtd: m25p80: Call spi_mem_get_name() to let controller set a custom name By calling spi_mem_get_name(), the driver of the (Q)SPI controller can set a custom name for the memory device if necessary. This is useful to keep mtdparts compatible when controller drivers are ported from the MTD to the SPI layer. Suggested-by: Boris Brezillon Signed-off-by: Frieder Schrempf Acked-by: Boris Brezillon Signed-off-by: Mark Brown --- drivers/mtd/devices/m25p80.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index e84563d2067f..aac488008216 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -202,6 +202,9 @@ static int m25p_probe(struct spi_mem *spimem) if (data && data->name) nor->mtd.name = data->name; + if (!nor->mtd.name) + nor->mtd.name = spi_mem_get_name(spimem); + /* For some (historical?) reason many platforms provide two different * names in flash_platform_data: "name" and "type". Quite often name is * set to "m25p80" and then "type" provides a real chip name. From 401c0d7712eb3189023efc9c0708a2ac984ed62e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 3 Aug 2018 11:50:39 +0200 Subject: [PATCH 40/40] spi: spi-mem: Constify spi_mem->name There is no reason to make spi_mem->name modifiable. Moreover, spi_mem_ops->get_name() returns a const char *, which generates a gcc warning when assigning the value returned by spi_mem_ops->get_name() to spi_mem->name. Fixes: 5d27a9c8ea9e ("spi: spi-mem: Extend the SPI mem interface to set a custom memory name") Reported-by: Stephen Rothwell Signed-off-by: Boris Brezillon Signed-off-by: Mark Brown --- include/linux/spi/spi-mem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 0d64ccc4e584..62722fb7472d 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -134,7 +134,7 @@ struct spi_mem_op { struct spi_mem { struct spi_device *spi; void *drvpriv; - char *name; + const char *name; }; /**