From fc9ba6e3e28afcedf3bb3105b1d8cbc5c183c2a3 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 26 Mar 2019 22:56:25 -0700 Subject: spi: tegra114: use packed mode for 32 bits per word Fixes: Use packed mode for 32 bits per word transfers to increase performance as each packet is a full 32-bit word. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index a76acedd7e2f..929358e3487a 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -259,7 +259,7 @@ static unsigned tegra_spi_calculate_curr_xfer_param( tspi->bytes_per_word = DIV_ROUND_UP(bits_per_word, 8); - if (bits_per_word == 8 || bits_per_word == 16) { + if (bits_per_word == 8 || bits_per_word == 16 || bits_per_word == 32) { tspi->is_packed = 1; tspi->words_per_32bit = 32/bits_per_word; } else { -- cgit v1.2.3 From 2b17a3c759e5e2ed3faafb69e243ec312e3bf0da Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Tue, 26 Mar 2019 22:56:33 -0700 Subject: spi: tegra114: add SPI_LSB_FIRST support Tegra SPI controller supports lsb first mode. Default is MSB bit first and on selection of SPI_LSB_FIRST through SPI mode transmission happens with LSB bit first. This patch adds SPI_LSB_FIRST flag to mode_bits and also configures it on request. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 929358e3487a..0c52aeeb1d4f 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -696,6 +696,11 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, else if (req_mode == SPI_MODE_3) command1 |= SPI_CONTROL_MODE_3; + if (spi->mode & SPI_LSB_FIRST) + command1 |= SPI_LSBIT_FE; + else + command1 &= ~SPI_LSBIT_FE; + if (tspi->cs_control) { if (tspi->cs_control != spi) tegra_spi_writel(tspi, command1, SPI_COMMAND1); @@ -1047,7 +1052,7 @@ static int tegra_spi_probe(struct platform_device *pdev) master->max_speed_hz = 25000000; /* 25MHz */ /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; master->setup = tegra_spi_setup; master->transfer_one_message = tegra_spi_transfer_one_message; master->num_chipselect = MAX_CHIP_SELECT; -- cgit v1.2.3 From a0253c8fa18129f423c22f175a83d81423e60715 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Thu, 4 Apr 2019 17:14:04 -0700 Subject: spi: tegra114: dump SPI registers during timeout This patch dumps SPI registers on transfer error or timeout for debug purpose. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 8c33bf056196..99019f6d2d84 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -874,6 +874,20 @@ static void tegra_spi_transfer_end(struct spi_device *spi) tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); } +static void tegra_spi_dump_regs(struct tegra_spi_data *tspi) +{ + dev_dbg(tspi->dev, "============ SPI REGISTER DUMP ============\n"); + dev_dbg(tspi->dev, "Command1: 0x%08x | Command2: 0x%08x\n", + tegra_spi_readl(tspi, SPI_COMMAND1), + tegra_spi_readl(tspi, SPI_COMMAND2)); + dev_dbg(tspi->dev, "DMA_CTL: 0x%08x | DMA_BLK: 0x%08x\n", + tegra_spi_readl(tspi, SPI_DMA_CTL), + tegra_spi_readl(tspi, SPI_DMA_BLK)); + dev_dbg(tspi->dev, "TRANS_STAT: 0x%08x | FIFO_STATUS: 0x%08x\n", + tegra_spi_readl(tspi, SPI_TRANS_STATUS), + tegra_spi_readl(tspi, SPI_FIFO_STATUS)); +} + static int tegra_spi_transfer_one_message(struct spi_master *master, struct spi_message *msg) { @@ -920,6 +934,7 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, (tspi->cur_direction & DATA_DIR_RX)) dmaengine_terminate_all(tspi->rx_dma_chan); ret = -EIO; + tegra_spi_dump_regs(tspi); tegra_spi_flush_fifos(tspi); reset_control_assert(tspi->rst); udelay(2); @@ -930,6 +945,7 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, if (tspi->tx_status || tspi->rx_status) { dev_err(tspi->dev, "Error in Transfer\n"); ret = -EIO; + tegra_spi_dump_regs(tspi); goto complete_xfer; } msg->actual_length += xfer->len; @@ -971,6 +987,7 @@ static irqreturn_t handle_cpu_based_xfer(struct tegra_spi_data *tspi) tspi->status_reg); dev_err(tspi->dev, "CpuXfer 0x%08x:0x%08x\n", tspi->command1_reg, tspi->dma_control_reg); + tegra_spi_dump_regs(tspi); tegra_spi_flush_fifos(tspi); complete(&tspi->xfer_completion); spin_unlock_irqrestore(&tspi->lock, flags); @@ -1045,6 +1062,7 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_spi_data *tspi) tspi->status_reg); dev_err(tspi->dev, "DmaXfer 0x%08x:0x%08x\n", tspi->command1_reg, tspi->dma_control_reg); + tegra_spi_dump_regs(tspi); tegra_spi_flush_fifos(tspi); complete(&tspi->xfer_completion); spin_unlock_irqrestore(&tspi->lock, flags); -- cgit v1.2.3 From f0a0bc90c6e7060778911c2b55d085105809d6cf Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Thu, 4 Apr 2019 17:14:05 -0700 Subject: spi: tegra114: set supported bits per word Tegra SPI supports 4 through 32 bits per word. This patch sets bits_per_word_mask accordingly to support transfer with these bits per word. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 99019f6d2d84..c2ebf1ae632d 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1151,6 +1151,7 @@ static int tegra_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = tegra_spi_setup; master->transfer_one_message = tegra_spi_transfer_one_message; master->num_chipselect = MAX_CHIP_SELECT; -- cgit v1.2.3 From 76457eea24db3c7d51f623c08838bbffda6ac3ad Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Thu, 4 Apr 2019 17:14:01 -0700 Subject: spi: tegra114: use unpacked mode for below 4 bytes Packed mode expects minimum transfer length of 4 bytes. This patch fixes this by using unpacked mode for transfers less than 4 bytes. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index c2ebf1ae632d..ea67ff3b5b2c 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -259,7 +259,8 @@ static unsigned tegra_spi_calculate_curr_xfer_param( tspi->bytes_per_word = DIV_ROUND_UP(bits_per_word, 8); - if (bits_per_word == 8 || bits_per_word == 16 || bits_per_word == 32) { + if ((bits_per_word == 8 || bits_per_word == 16 || + bits_per_word == 32) && t->len > 3) { tspi->is_packed = 1; tspi->words_per_32bit = 32/bits_per_word; } else { -- cgit v1.2.3 From d9088966c851ecbe820c053b347e8d565e4fa05c Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Thu, 4 Apr 2019 17:14:06 -0700 Subject: spi: tegra114: set bus number based on id This patch sets SPI device id from the device tree as the bus number. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index ea67ff3b5b2c..402799c7f6b7 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1137,6 +1137,7 @@ static int tegra_spi_probe(struct platform_device *pdev) struct tegra_spi_data *tspi; struct resource *r; int ret, spi_irq; + int bus_num; master = spi_alloc_master(&pdev->dev, sizeof(*tspi)); if (!master) { @@ -1157,6 +1158,9 @@ static int tegra_spi_probe(struct platform_device *pdev) master->transfer_one_message = tegra_spi_transfer_one_message; master->num_chipselect = MAX_CHIP_SELECT; master->auto_runtime_pm = true; + bus_num = of_alias_get_id(pdev->dev.of_node, "spi"); + if (bus_num >= 0) + master->bus_num = bus_num; tspi->master = master; tspi->dev = &pdev->dev; -- cgit v1.2.3 From 9877a347f2056f7aa4e770de9a20048ad288f545 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Thu, 4 Apr 2019 17:14:07 -0700 Subject: spi: tegra114: add dual mode support This patch adds support for dual mode SPI transfer. Dual mode uses both MOSI and MISO lines in parallel where the data is interleaved on MOSI and MISO lines increasing the throughput. Packet from Tx FIFO is transmitted on both MOSI and MISO lines and packet to Rx FIFO is received from both MOSI and MISO lines. Even bits are transmitted or received on the MOSI data line and odd bits are transmitted or received on the MISO data line. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 402799c7f6b7..1e749ce13029 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -786,6 +786,11 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi, total_fifo_words = tegra_spi_calculate_curr_xfer_param(spi, tspi, t); + if (t->rx_nbits == SPI_NBITS_DUAL || t->tx_nbits == SPI_NBITS_DUAL) + command1 |= SPI_BOTH_EN_BIT; + else + command1 &= ~SPI_BOTH_EN_BIT; + if (tspi->is_packed) command1 |= SPI_PACKED; else @@ -1152,7 +1157,8 @@ static int tegra_spi_probe(struct platform_device *pdev) master->max_speed_hz = 25000000; /* 25MHz */ /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST | + SPI_TX_DUAL | SPI_RX_DUAL; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = tegra_spi_setup; master->transfer_one_message = tegra_spi_transfer_one_message; -- cgit v1.2.3 From 9d199231b000414e420a35912760f2d67e9c56d7 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Thu, 4 Apr 2019 17:14:08 -0700 Subject: spi: tegra114: add 3 wire transfer mode support This patch adds 3 wire transfer support to SPI mode list along with its implementation. 3 wire or Bi-directional mode uses only one serial data pin for the transfer. SPI in master mode uses MOSI data line only and MISO data line is not used. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 1e749ce13029..751672b3bc16 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -754,6 +754,11 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, else command1 &= ~SPI_LSBIT_FE; + if (spi->mode & SPI_3WIRE) + command1 |= SPI_BIDIROE; + else + command1 &= ~SPI_BIDIROE; + if (tspi->cs_control) { if (tspi->cs_control != spi) tegra_spi_writel(tspi, command1, SPI_COMMAND1); @@ -1158,7 +1163,7 @@ static int tegra_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST | - SPI_TX_DUAL | SPI_RX_DUAL; + SPI_TX_DUAL | SPI_RX_DUAL | SPI_3WIRE; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = tegra_spi_setup; master->transfer_one_message = tegra_spi_transfer_one_message; -- cgit v1.2.3 From fa28fd3421373794e98eb3e511969fea5a8b2fc3 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Thu, 4 Apr 2019 17:14:12 -0700 Subject: spi: tegra114: add support for interrupt mask This patch creates tegra_spi_soc_data structure to maintain and implement SPI HW feature differences between different Tegra chips and also creates a separate compatible string for T124/T210. Tegra210 and later has a separate interrupt mask register SPI_INTR_MASK for enabling or disabling interrupts while Tegra124 and prior uses interrupt enable bits in SPI_DMA_CTL register. This patch creates flag has_intr_mask_reg in tegra_spi_soc_data to identify this and implements accordingly. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 53 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 751672b3bc16..b8c6393e2190 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -149,6 +149,8 @@ #define SPI_TX_FIFO 0x108 #define SPI_RX_FIFO 0x188 +#define SPI_INTR_MASK 0x18c +#define SPI_INTR_ALL_MASK (0x1fUL << 25) #define MAX_CHIP_SELECT 4 #define SPI_FIFO_DEPTH 64 #define DATA_DIR_TX (1 << 0) @@ -161,6 +163,10 @@ #define MAX_HOLD_CYCLES 16 #define SPI_DEFAULT_SPEED 25000000 +struct tegra_spi_soc_data { + bool has_intr_mask_reg; +}; + struct tegra_spi_data { struct device *dev; struct spi_master *master; @@ -211,6 +217,7 @@ struct tegra_spi_data { u32 *tx_dma_buf; dma_addr_t tx_dma_phys; struct dma_async_tx_descriptor *tx_dma_desc; + const struct tegra_spi_soc_data *soc_data; }; static int tegra_spi_runtime_suspend(struct device *dev); @@ -554,11 +561,13 @@ static int tegra_spi_start_dma_based_transfer( dma_burst = 8; } - if (tspi->cur_direction & DATA_DIR_TX) - val |= SPI_IE_TX; + if (!tspi->soc_data->has_intr_mask_reg) { + if (tspi->cur_direction & DATA_DIR_TX) + val |= SPI_IE_TX; - if (tspi->cur_direction & DATA_DIR_RX) - val |= SPI_IE_RX; + if (tspi->cur_direction & DATA_DIR_RX) + val |= SPI_IE_RX; + } tegra_spi_writel(tspi, val, SPI_DMA_CTL); tspi->dma_control_reg = val; @@ -847,6 +856,12 @@ static int tegra_spi_setup(struct spi_device *spi) return ret; } + if (tspi->soc_data->has_intr_mask_reg) { + val = tegra_spi_readl(tspi, SPI_INTR_MASK); + val &= ~SPI_INTR_ALL_MASK; + tegra_spi_writel(tspi, val, SPI_INTR_MASK); + } + spin_lock_irqsave(&tspi->lock, flags); val = tspi->def_command1_reg; if (spi->mode & SPI_CS_HIGH) @@ -1135,8 +1150,29 @@ static irqreturn_t tegra_spi_isr(int irq, void *context_data) return IRQ_WAKE_THREAD; } +static struct tegra_spi_soc_data tegra114_spi_soc_data = { + .has_intr_mask_reg = false, +}; + +static struct tegra_spi_soc_data tegra124_spi_soc_data = { + .has_intr_mask_reg = false, +}; + +static struct tegra_spi_soc_data tegra210_spi_soc_data = { + .has_intr_mask_reg = true, +}; + static const struct of_device_id tegra_spi_of_match[] = { - { .compatible = "nvidia,tegra114-spi", }, + { + .compatible = "nvidia,tegra114-spi", + .data = &tegra114_spi_soc_data, + }, { + .compatible = "nvidia,tegra124-spi", + .data = &tegra124_spi_soc_data, + }, { + .compatible = "nvidia,tegra210-spi", + .data = &tegra210_spi_soc_data, + }, {} }; MODULE_DEVICE_TABLE(of, tegra_spi_of_match); @@ -1177,6 +1213,13 @@ static int tegra_spi_probe(struct platform_device *pdev) tspi->dev = &pdev->dev; spin_lock_init(&tspi->lock); + tspi->soc_data = of_device_get_match_data(&pdev->dev); + if (!tspi->soc_data) { + dev_err(&pdev->dev, "unsupported tegra\n"); + ret = -ENODEV; + goto exit_free_master; + } + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); tspi->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(tspi->base)) { -- cgit v1.2.3