Fixes: Use PIO bit in SPI_COMMAND1 register for PIO mode.
Current driver uses DMA_EN instead of PIO bit.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index a76acedd7e2f..5a21bc808bb6 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -570,8 +570,9 @@ static int tegra_spi_start_cpu_based_transfer(
tspi->is_curr_dma_xfer = false;
- val |= SPI_DMA_EN;
- tegra_spi_writel(tspi, val, SPI_DMA_CTL);
+ val = tspi->command1_reg;
+ val |= SPI_PIO;
+ tegra_spi_writel(tspi, val, SPI_COMMAND1);
return 0;
}
--
2.7.4
Fixes: Clear packed bit when not using packed mode.
Packed bit is not cleared when not using packed mode. This results
in transfer timeouts for the unpacked mode transfers followed by the
packed mode transfers.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 5a21bc808bb6..82431115b7c0 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -731,6 +731,8 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi,
if (tspi->is_packed)
command1 |= SPI_PACKED;
+ else
+ command1 &= ~SPI_PACKED;
command1 &= ~(SPI_CS_SEL_MASK | SPI_TX_EN | SPI_RX_EN);
tspi->cur_direction = 0;
--
2.7.4
Fixes: terminate DMA and perform controller reset on transfer timeout
to clear the FIFO's and errors.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 716587b663a3..0d20fc489134 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -871,7 +871,16 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
if (WARN_ON(ret == 0)) {
dev_err(tspi->dev,
"spi transfer timeout, err %d\n", ret);
+ if (tspi->is_curr_dma_xfer &&
+ (tspi->cur_direction & DATA_DIR_TX))
+ dmaengine_terminate_all(tspi->tx_dma_chan);
+ if (tspi->is_curr_dma_xfer &&
+ (tspi->cur_direction & DATA_DIR_RX))
+ dmaengine_terminate_all(tspi->rx_dma_chan);
ret = -EIO;
+ reset_control_assert(tspi->rst);
+ udelay(2);
+ reset_control_deassert(tspi->rst);
goto complete_xfer;
}
--
2.7.4
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 <[email protected]>
---
drivers/spi/spi-tegra114.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index fdba302eb3b6..dc63536dbda4 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 {
--
2.7.4
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 <[email protected]>
---
drivers/spi/spi-tegra114.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index d39f7b05b824..335c594d5b74 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -787,6 +787,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
@@ -1142,7 +1147,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->setup = tegra_spi_setup;
master->transfer_one_message = tegra_spi_transfer_one_message;
master->num_chipselect = MAX_CHIP_SELECT;
--
2.7.4
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/T186.
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 <[email protected]>
---
drivers/spi/spi-tegra114.c | 53 +++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 48 insertions(+), 5 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 9b9d4b9e1f3e..68fee474580b 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;
@@ -848,6 +857,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)
@@ -1126,8 +1141,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);
@@ -1168,6 +1204,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)) {
--
2.7.4
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 <[email protected]>
---
drivers/spi/spi-tegra114.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 01efb615a196..94acef0f5ea5 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1154,6 +1154,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_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;
master->num_chipselect = MAX_CHIP_SELECT;
--
2.7.4
This patch adds support for HW based CS control.
Tegra SPI controller supports both HW and SW based CS control
transfers.
Tegra SPI driver default uses SW CS control for transfers and HW CS
control can be enabled through SPI client device node DT property
nvidia,enable-hw-based-cs and is used only for single transfers.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 71 +++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 64 insertions(+), 7 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 68fee474580b..86c34f02d13a 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -167,6 +167,10 @@ struct tegra_spi_soc_data {
bool has_intr_mask_reg;
};
+struct tegra_spi_client_data {
+ bool is_hw_based_cs;
+};
+
struct tegra_spi_data {
struct device *dev;
struct spi_master *master;
@@ -193,6 +197,7 @@ struct tegra_spi_data {
unsigned dma_buf_size;
unsigned max_buf_size;
bool is_curr_dma_xfer;
+ bool use_hw_based_cs;
struct completion rx_dma_complete;
struct completion tx_dma_complete;
@@ -723,9 +728,11 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi,
}
static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
- struct spi_transfer *t, bool is_first_of_msg)
+ struct spi_transfer *t, bool is_first_of_msg,
+ bool is_single_xfer)
{
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
+ struct tegra_spi_client_data *cdata = spi->controller_data;
u32 speed = t->speed_hz;
u8 bits_per_word = t->bits_per_word;
u32 command1;
@@ -776,11 +783,19 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
} else
tegra_spi_writel(tspi, command1, SPI_COMMAND1);
- command1 |= SPI_CS_SW_HW;
- if (spi->mode & SPI_CS_HIGH)
- command1 |= SPI_CS_SW_VAL;
- else
- command1 &= ~SPI_CS_SW_VAL;
+ tspi->use_hw_based_cs = false;
+ if (cdata && cdata->is_hw_based_cs && is_single_xfer)
+ tspi->use_hw_based_cs = true;
+
+ if (!tspi->use_hw_based_cs) {
+ command1 |= SPI_CS_SW_HW;
+ if (spi->mode & SPI_CS_HIGH)
+ command1 |= SPI_CS_SW_VAL;
+ else
+ command1 &= ~SPI_CS_SW_VAL;
+ } else {
+ command1 &= ~(SPI_CS_SW_HW | SPI_CS_SW_VAL);
+ }
tegra_spi_writel(tspi, 0, SPI_COMMAND2);
} else {
@@ -838,9 +853,41 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi,
return ret;
}
+static struct tegra_spi_client_data
+ *tegra_spi_parse_cdata_dt(struct spi_device *spi)
+{
+ struct tegra_spi_client_data *cdata;
+ struct device_node *slave_np;
+
+ slave_np = spi->dev.of_node;
+ if (!slave_np) {
+ dev_dbg(&spi->dev, "device node not found\n");
+ return NULL;
+ }
+
+ cdata = kzalloc(sizeof(*cdata), GFP_KERNEL);
+ if (!cdata)
+ return NULL;
+
+ if (of_property_read_bool(slave_np, "nvidia,enable-hw-based-cs"))
+ cdata->is_hw_based_cs = true;
+
+ return cdata;
+}
+
+static void tegra_spi_cleanup(struct spi_device *spi)
+{
+ struct tegra_spi_client_data *cdata = spi->controller_data;
+
+ spi->controller_data = NULL;
+ if (spi->dev.of_node)
+ kfree(cdata);
+}
+
static int tegra_spi_setup(struct spi_device *spi)
{
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
+ struct tegra_spi_client_data *cdata = spi->controller_data;
u32 val;
unsigned long flags;
int ret;
@@ -851,9 +898,15 @@ static int tegra_spi_setup(struct spi_device *spi)
spi->mode & SPI_CPHA ? "" : "~",
spi->max_speed_hz);
+ if (!cdata) {
+ cdata = tegra_spi_parse_cdata_dt(spi);
+ spi->controller_data = cdata;
+ }
+
ret = pm_runtime_get_sync(tspi->dev);
if (ret < 0) {
dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
+ tegra_spi_cleanup(spi);
return ret;
}
@@ -909,18 +962,21 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
struct tegra_spi_data *tspi = spi_master_get_devdata(master);
struct spi_transfer *xfer;
struct spi_device *spi = msg->spi;
+ int single_xfer;
int ret;
bool skip = false;
msg->status = 0;
msg->actual_length = 0;
+ single_xfer = list_is_singular(&msg->transfers);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
u32 cmd1;
reinit_completion(&tspi->xfer_completion);
- cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg);
+ cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg,
+ single_xfer);
if (!xfer->len) {
ret = 0;
@@ -1193,6 +1249,7 @@ static int tegra_spi_probe(struct platform_device *pdev)
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->cleanup = tegra_spi_cleanup;
master->transfer_one_message = tegra_spi_transfer_one_message;
master->num_chipselect = MAX_CHIP_SELECT;
master->auto_runtime_pm = true;
--
2.7.4
Tegra SPI controller has TX_CLK_TAP_DELAY and RX_CLK_TAP_DELAY in
COMMAND2 register to tune delay of the clock going out to external
device during transmit and also for the clock coming in from external
device during receive.
TX/RX clock tap delays may vary based on the trace lengths of the
platform design for each of the slaves on the SPI bus.
This patch adds support for configuring TX/RX clock delays specified
through device tree properties.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index e01962344bde..725d60364ec6 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -174,6 +174,8 @@ struct tegra_spi_client_data {
int cs_setup_clk_count;
int cs_hold_clk_count;
int cs_inactive_cycles;
+ int tx_clk_tap_delay;
+ int rx_clk_tap_delay;
};
struct tegra_spi_data {
@@ -215,8 +217,10 @@ struct tegra_spi_data {
u32 command1_reg;
u32 dma_control_reg;
u32 def_command1_reg;
+ u32 def_command2_reg;
u32 spi_cs_timing1;
u32 spi_cs_timing2;
+ u8 last_used_cs;
struct completion xfer_completion;
struct spi_transfer *curr_xfer;
@@ -780,7 +784,9 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
u32 speed = t->speed_hz;
u8 bits_per_word = t->bits_per_word;
u32 command1;
+ u32 command2;
int req_mode;
+ u32 tx_tap = 0, rx_tap = 0;
if (speed != tspi->cur_speed) {
clk_set_rate(tspi->clk, speed);
@@ -843,7 +849,18 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
command1 &= ~(SPI_CS_SW_HW | SPI_CS_SW_VAL);
}
- tegra_spi_writel(tspi, 0, SPI_COMMAND2);
+ if (tspi->last_used_cs != spi->chip_select) {
+ if (cdata && cdata->tx_clk_tap_delay)
+ tx_tap = cdata->tx_clk_tap_delay;
+ if (cdata && cdata->rx_clk_tap_delay)
+ rx_tap = cdata->rx_clk_tap_delay;
+ command2 = SPI_TX_TAP_DELAY(tx_tap) |
+ SPI_RX_TAP_DELAY(rx_tap);
+ if (command2 != tspi->def_command2_reg)
+ tegra_spi_writel(tspi, command2, SPI_COMMAND2);
+ tspi->last_used_cs = spi->chip_select;
+ }
+
} else {
command1 = tspi->command1_reg;
command1 &= ~SPI_BIT_LENGTH(~0);
@@ -923,6 +940,10 @@ static struct tegra_spi_client_data
&cdata->cs_hold_clk_count);
of_property_read_u32(slave_np, "nvidia,cs-inactive-cycles",
&cdata->cs_inactive_cycles);
+ of_property_read_u32(slave_np, "nvidia,tx-clk-tap-delay",
+ &cdata->tx_clk_tap_delay);
+ of_property_read_u32(slave_np, "nvidia,rx-clk-tap-delay",
+ &cdata->rx_clk_tap_delay);
return cdata;
}
@@ -1379,6 +1400,8 @@ static int tegra_spi_probe(struct platform_device *pdev)
tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1);
tspi->spi_cs_timing1 = tegra_spi_readl(tspi, SPI_CS_TIMING1);
tspi->spi_cs_timing2 = tegra_spi_readl(tspi, SPI_CS_TIMING2);
+ tspi->def_command2_reg = tegra_spi_readl(tspi, SPI_COMMAND2);
+ tspi->last_used_cs = master->num_chipselect + 1;
pm_runtime_put(&pdev->dev);
ret = request_threaded_irq(tspi->irq, tegra_spi_isr,
tegra_spi_isr_thread, IRQF_ONESHOT,
@@ -1451,6 +1474,8 @@ static int tegra_spi_resume(struct device *dev)
return ret;
}
tegra_spi_writel(tspi, tspi->command1_reg, SPI_COMMAND1);
+ tegra_spi_writel(tspi, tspi->def_command2_reg, SPI_COMMAND2);
+ tspi->last_used_cs = master->num_chipselect + 1;
pm_runtime_put(dev);
return spi_master_resume(master);
--
2.7.4
With SW CS, during transfer completion CS is de-asserted by writing the
default command1 register value to SPI_COMMAND1 register. With this both
mode and CS state are set at the same time and if current transfer mode
is different to default SPI mode and if mode change happens prior to CS
de-assert, clock polarity can change while CS is active before transfer
finishes.
This causes Slave to see spurious clock edges resulting in data mismatch.
This patch fixes this by de-asserting CS before writing SPI_COMMAND1 to
its default value so through out the transfer it will be in same SPI mode.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 9b216e9d6079..e1669ab3b0fe 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1147,6 +1147,12 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
if (ret < 0 || skip) {
if (cstate->cs_gpio_valid)
gpio_set_value(spi->cs_gpio, cs_val);
+ if (cs_val && !tspi->use_hw_based_cs)
+ tspi->command1_reg |= SPI_CS_SW_VAL;
+ else
+ tspi->command1_reg &= ~SPI_CS_SW_VAL;
+ tegra_spi_writel(tspi, tspi->command1_reg,
+ SPI_COMMAND1);
tegra_spi_writel(tspi, tspi->def_command1_reg,
SPI_COMMAND1);
tegra_spi_transfer_delay(xfer->delay_usecs);
@@ -1158,6 +1164,12 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
else {
if (cstate->cs_gpio_valid)
gpio_set_value(spi->cs_gpio, cs_val);
+ if (cs_val && !tspi->use_hw_based_cs)
+ tspi->command1_reg |= SPI_CS_SW_VAL;
+ else
+ tspi->command1_reg &= ~SPI_CS_SW_VAL;
+ tegra_spi_writel(tspi, tspi->command1_reg,
+ SPI_COMMAND1);
tegra_spi_writel(tspi, tspi->def_command1_reg,
SPI_COMMAND1);
tegra_spi_transfer_delay(xfer->delay_usecs);
@@ -1165,6 +1177,12 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
} else if (xfer->cs_change) {
if (cstate->cs_gpio_valid)
gpio_set_value(spi->cs_gpio, cs_val);
+ if (cs_val && !tspi->use_hw_based_cs)
+ tspi->command1_reg |= SPI_CS_SW_VAL;
+ else
+ tspi->command1_reg &= ~SPI_CS_SW_VAL;
+ tegra_spi_writel(tspi, tspi->command1_reg,
+ SPI_COMMAND1);
tegra_spi_writel(tspi, tspi->def_command1_reg,
SPI_COMMAND1);
tegra_spi_transfer_delay(xfer->delay_usecs);
--
2.7.4
This patch adds supports for chip select control using GPIO if valid
CS gpio exists rather than controlling from the SPI controller.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 725d60364ec6..9b216e9d6079 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -23,6 +23,7 @@
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/err.h>
+#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -178,6 +179,10 @@ struct tegra_spi_client_data {
int rx_clk_tap_delay;
};
+struct tegra_spi_client_state {
+ bool cs_gpio_valid;
+};
+
struct tegra_spi_data {
struct device *dev;
struct spi_master *master;
@@ -781,6 +786,7 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
{
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
struct tegra_spi_client_data *cdata = spi->controller_data;
+ struct tegra_spi_client_state *cstate = spi->controller_state;
u32 speed = t->speed_hz;
u8 bits_per_word = t->bits_per_word;
u32 command1;
@@ -849,6 +855,12 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
command1 &= ~(SPI_CS_SW_HW | SPI_CS_SW_VAL);
}
+ if (cstate->cs_gpio_valid) {
+ int val = (spi->mode & SPI_CS_HIGH) ? 1 : 0;
+
+ gpio_set_value(spi->cs_gpio, val);
+ }
+
if (tspi->last_used_cs != spi->chip_select) {
if (cdata && cdata->tx_clk_tap_delay)
tx_tap = cdata->tx_clk_tap_delay;
@@ -950,7 +962,12 @@ static struct tegra_spi_client_data
static void tegra_spi_cleanup(struct spi_device *spi)
{
struct tegra_spi_client_data *cdata = spi->controller_data;
+ struct tegra_spi_client_state *cstate = spi->controller_state;
+ spi->controller_state = NULL;
+ if (cstate && cstate->cs_gpio_valid)
+ gpio_free(spi->cs_gpio);
+ kfree(cstate);
spi->controller_data = NULL;
if (spi->dev.of_node)
kfree(cdata);
@@ -960,6 +977,7 @@ static int tegra_spi_setup(struct spi_device *spi)
{
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
struct tegra_spi_client_data *cdata = spi->controller_data;
+ struct tegra_spi_client_state *cstate = spi->controller_state;
u32 val;
unsigned long flags;
int ret;
@@ -970,11 +988,41 @@ static int tegra_spi_setup(struct spi_device *spi)
spi->mode & SPI_CPHA ? "" : "~",
spi->max_speed_hz);
+ if (!cstate) {
+ cstate = kzalloc(sizeof(*cstate), GFP_KERNEL);
+ if (!cstate)
+ return -ENOMEM;
+ spi->controller_state = cstate;
+ }
+
if (!cdata) {
cdata = tegra_spi_parse_cdata_dt(spi);
spi->controller_data = cdata;
}
+ if (spi->master->cs_gpios && gpio_is_valid(spi->cs_gpio)) {
+ if (!cstate->cs_gpio_valid) {
+ int gpio_flag = GPIOF_OUT_INIT_HIGH;
+
+ if (spi->mode & SPI_CS_HIGH)
+ gpio_flag = GPIOF_OUT_INIT_LOW;
+
+ ret = gpio_request_one(spi->cs_gpio, gpio_flag,
+ "cs_gpio");
+ if (ret < 0) {
+ dev_err(&spi->dev,
+ "GPIO request failed: %d\n", ret);
+ tegra_spi_cleanup(spi);
+ return ret;
+ }
+ cstate->cs_gpio_valid = true;
+ } else {
+ int val = (spi->mode & SPI_CS_HIGH) ? 0 : 1;
+
+ gpio_set_value(spi->cs_gpio, val);
+ }
+ }
+
ret = pm_runtime_get_sync(tspi->dev);
if (ret < 0) {
dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
@@ -1034,9 +1082,11 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
struct tegra_spi_data *tspi = spi_master_get_devdata(master);
struct spi_transfer *xfer;
struct spi_device *spi = msg->spi;
+ struct tegra_spi_client_state *cstate = spi->controller_state;
int single_xfer;
int ret;
bool skip = false;
+ int cs_val;
msg->status = 0;
msg->actual_length = 0;
@@ -1093,7 +1143,10 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
msg->actual_length += xfer->len;
complete_xfer:
+ cs_val = (spi->mode & SPI_CS_HIGH) ? 0 : 1;
if (ret < 0 || skip) {
+ if (cstate->cs_gpio_valid)
+ gpio_set_value(spi->cs_gpio, cs_val);
tegra_spi_writel(tspi, tspi->def_command1_reg,
SPI_COMMAND1);
tegra_spi_transfer_delay(xfer->delay_usecs);
@@ -1103,11 +1156,15 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
if (xfer->cs_change)
tspi->cs_control = spi;
else {
+ if (cstate->cs_gpio_valid)
+ gpio_set_value(spi->cs_gpio, cs_val);
tegra_spi_writel(tspi, tspi->def_command1_reg,
SPI_COMMAND1);
tegra_spi_transfer_delay(xfer->delay_usecs);
}
} else if (xfer->cs_change) {
+ if (cstate->cs_gpio_valid)
+ gpio_set_value(spi->cs_gpio, cs_val);
tegra_spi_writel(tspi, tspi->def_command1_reg,
SPI_COMMAND1);
tegra_spi_transfer_delay(xfer->delay_usecs);
--
2.7.4
Some SPI devices expects SPI transfers to be in Least significant byte
first order and some devices expect Most significant byte first order.
This patch adds SPI_LSBYTE_FIRST to supported mode list and implements
configuration accordingly.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index e1669ab3b0fe..a2e809af96da 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -827,6 +827,11 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
else
command1 &= ~SPI_LSBIT_FE;
+ if (spi->mode & SPI_LSBYTE_FIRST)
+ command1 |= SPI_LSBYTE_FE;
+ else
+ command1 &= ~SPI_LSBYTE_FE;
+
if (spi->mode & SPI_3WIRE)
command1 |= SPI_BIDIROE;
else
@@ -1393,7 +1398,8 @@ 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_3WIRE;
+ SPI_TX_DUAL | SPI_RX_DUAL | SPI_3WIRE |
+ SPI_LSBYTE_FIRST;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
master->setup = tegra_spi_setup;
master->cleanup = tegra_spi_cleanup;
--
2.7.4
Some slaves may need certain CS setup time, hold time, CS inactive
delay between the packets. Tegra SPI controller supports configuring
these CS timing parameters and are applicable when using HW CS.
This patch adds support for configuring these HW CS timing parameters
through device tree properties.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 61 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 57 insertions(+), 4 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 86c34f02d13a..e01962344bde 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -95,8 +95,10 @@
(reg = (((val) & 0x1) << ((cs) * 8 + 5)) | \
((reg) & ~(1 << ((cs) * 8 + 5))))
#define SPI_SET_CYCLES_BETWEEN_PACKETS(reg, cs, val) \
- (reg = (((val) & 0xF) << ((cs) * 8)) | \
- ((reg) & ~(0xF << ((cs) * 8))))
+ (reg = (((val) & 0x1F) << ((cs) * 8)) | \
+ ((reg) & ~(0x1F << ((cs) * 8))))
+#define MAX_SETUP_HOLD_CYCLES 16
+#define MAX_INACTIVE_CYCLES 32
#define SPI_TRANS_STATUS 0x010
#define SPI_BLK_CNT(val) (((val) >> 0) & 0xFFFF)
@@ -169,6 +171,9 @@ struct tegra_spi_soc_data {
struct tegra_spi_client_data {
bool is_hw_based_cs;
+ int cs_setup_clk_count;
+ int cs_hold_clk_count;
+ int cs_inactive_cycles;
};
struct tegra_spi_data {
@@ -210,6 +215,8 @@ struct tegra_spi_data {
u32 command1_reg;
u32 dma_control_reg;
u32 def_command1_reg;
+ u32 spi_cs_timing1;
+ u32 spi_cs_timing2;
struct completion xfer_completion;
struct spi_transfer *curr_xfer;
@@ -727,6 +734,43 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi,
dma_release_channel(dma_chan);
}
+static void tegra_spi_set_hw_cs_timing(struct spi_device *spi)
+{
+ struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
+ struct tegra_spi_client_data *cdata = spi->controller_data;
+ u32 setup_dly;
+ u32 hold_dly;
+ u32 setup_hold;
+ u32 spi_cs_timing;
+ u32 inactive_cycles;
+ u8 cs_state;
+
+ setup_dly = min(cdata->cs_setup_clk_count, MAX_SETUP_HOLD_CYCLES);
+ hold_dly = min(cdata->cs_hold_clk_count, MAX_SETUP_HOLD_CYCLES);
+ setup_hold = SPI_SETUP_HOLD(setup_dly - 1, hold_dly - 1);
+ spi_cs_timing = SPI_CS_SETUP_HOLD(tspi->spi_cs_timing1,
+ spi->chip_select,
+ setup_hold);
+ if (tspi->spi_cs_timing1 != spi_cs_timing) {
+ tspi->spi_cs_timing1 = spi_cs_timing;
+ tegra_spi_writel(tspi, spi_cs_timing, SPI_CS_TIMING1);
+ }
+
+ spi_cs_timing = tspi->spi_cs_timing2;
+ inactive_cycles = min(cdata->cs_inactive_cycles, MAX_INACTIVE_CYCLES);
+ if (inactive_cycles)
+ inactive_cycles--;
+ cs_state = inactive_cycles ? 0 : 1;
+ SPI_SET_CS_ACTIVE_BETWEEN_PACKETS(spi_cs_timing, spi->chip_select,
+ cs_state);
+ SPI_SET_CYCLES_BETWEEN_PACKETS(spi_cs_timing, spi->chip_select,
+ inactive_cycles);
+ if (tspi->spi_cs_timing2 != spi_cs_timing) {
+ tspi->spi_cs_timing2 = spi_cs_timing;
+ tegra_spi_writel(tspi, spi_cs_timing, SPI_CS_TIMING2);
+ }
+}
+
static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
struct spi_transfer *t, bool is_first_of_msg,
bool is_single_xfer)
@@ -784,8 +828,10 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
tegra_spi_writel(tspi, command1, SPI_COMMAND1);
tspi->use_hw_based_cs = false;
- if (cdata && cdata->is_hw_based_cs && is_single_xfer)
+ if (cdata && cdata->is_hw_based_cs && is_single_xfer) {
tspi->use_hw_based_cs = true;
+ tegra_spi_set_hw_cs_timing(spi);
+ }
if (!tspi->use_hw_based_cs) {
command1 |= SPI_CS_SW_HW;
@@ -871,7 +917,12 @@ static struct tegra_spi_client_data
if (of_property_read_bool(slave_np, "nvidia,enable-hw-based-cs"))
cdata->is_hw_based_cs = true;
-
+ of_property_read_u32(slave_np, "nvidia,cs-setup-clk-count",
+ &cdata->cs_setup_clk_count);
+ of_property_read_u32(slave_np, "nvidia,cs-hold-clk-count",
+ &cdata->cs_hold_clk_count);
+ of_property_read_u32(slave_np, "nvidia,cs-inactive-cycles",
+ &cdata->cs_inactive_cycles);
return cdata;
}
@@ -1326,6 +1377,8 @@ static int tegra_spi_probe(struct platform_device *pdev)
reset_control_deassert(tspi->rst);
tspi->def_command1_reg = SPI_M_S;
tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1);
+ tspi->spi_cs_timing1 = tegra_spi_readl(tspi, SPI_CS_TIMING1);
+ tspi->spi_cs_timing2 = tegra_spi_readl(tspi, SPI_CS_TIMING2);
pm_runtime_put(&pdev->dev);
ret = request_threaded_irq(tspi->irq, tegra_spi_isr,
tegra_spi_isr_thread, IRQF_ONESHOT,
--
2.7.4
mode and mode_bits is declared as u16 and all bits are used.
This patch changes mode and mode_bits to be u32 to allow for more mode
configurations.
Some SPI Master controllers support configuring Least significant byte
first or Most significant byte first order for transfers. Also some SPI
slave devices expect bytes to be in Least significant first order and
some devices expect Most significant first order.
This patch creates SPI_LSBYTE_FIRST mode for this purpose.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
include/linux/spi/spi.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index a0975cf76cf6..0032aa47dea0 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -143,7 +143,7 @@ struct spi_device {
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
- u16 mode;
+ u32 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
@@ -164,6 +164,7 @@ struct spi_device {
#define SPI_TX_OCTAL 0x2000 /* transmit with 8 wires */
#define SPI_RX_OCTAL 0x4000 /* receive with 8 wires */
#define SPI_3WIRE_HIZ 0x8000 /* high impedance turnaround */
+#define SPI_LSBYTE_FIRST 0x10000 /* per-word bytes-on-wire */
int irq;
void *controller_state;
void *controller_data;
@@ -439,7 +440,7 @@ struct spi_controller {
u16 dma_alignment;
/* spi_device.mode flags understood by this controller driver */
- u16 mode_bits;
+ u32 mode_bits;
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
--
2.7.4
This patch adds Tegra SPI master tx and rx clock delay properties.
TX/RX clock delays may vary depending on the platform design trace lengths
for each client on the Tegra SPI bus. These properties helps to tune the
clock delays.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
.../devicetree/bindings/spi/nvidia,tegra114-spi.txt | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
index 6167c5234b64..2b84b7b726ce 100644
--- a/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
+++ b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
@@ -29,6 +29,12 @@ spi-client device controller properties:
- nvidia,cs-hold-clk-count: CS hold timing parameter.
- nvidia,cs-inactive-cycles: CS inactive delay in terms of clock between
transfers.
+- nvidia,tx-clk-tap-delay: Delays the clock going out to the external device
+ with this tap value. This property is used to tune the outgoing data from
+ Tegra SPI master with respect to outgoing Tegra SPI master clock.
+- nvidia,rx-clk-tap-delay: Delays the clock coming in from the external device
+ with this tap value. This property is used to adjust the Tegra SPI master
+ clock with respect to the data from the SPI slave device.
Example:
@@ -45,4 +51,14 @@ spi@7000d600 {
reset-names = "spi";
dmas = <&apbdma 16>, <&apbdma 16>;
dma-names = "rx", "tx";
+
+ <spi-client>@<bus_num> {
+ ...
+ ...
+ nvidia,cs-setup-clk-count = <10>;
+ nvidia,cs-hold-clk-count = <10>;
+ nvidia,rx-clk-tap-delay = <0>;
+ nvidia,tx-clk-tap-delay = <16>;
+ ...
+ };
};
--
2.7.4
This patch sets the SPI device id from the device tree as the
bus number.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 94acef0f5ea5..9b9d4b9e1f3e 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1138,6 +1138,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) {
@@ -1159,6 +1160,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;
--
2.7.4
This patch adds 3 wire transfer support to SPI mode list and also
implements it.
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 by the SPI.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 335c594d5b74..01efb615a196 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -755,6 +755,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);
@@ -1148,7 +1153,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->setup = tegra_spi_setup;
master->transfer_one_message = tegra_spi_transfer_one_message;
master->num_chipselect = MAX_CHIP_SELECT;
--
2.7.4
Fixes: Configure DMA burst size to be same as SPI TX/RX trigger levels
to avoid mismatch.
SPI FIFO trigger levels are calculated based on the transfer length.
So this patch moves DMA slave configuration to happen before start
of DMAs.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 52 ++++++++++++++++++++++++++--------------------
1 file changed, 30 insertions(+), 22 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 477828051aba..b7e71676a506 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -530,6 +530,8 @@ static int tegra_spi_start_dma_based_transfer(
u32 val;
unsigned int len;
int ret = 0;
+ u8 dma_burst;
+ struct dma_slave_config dma_sconfig = {0};
val = SPI_DMA_BLK_SET(tspi->curr_dma_words - 1);
tegra_spi_writel(tspi, val, SPI_DMA_BLK);
@@ -541,12 +543,16 @@ static int tegra_spi_start_dma_based_transfer(
len = tspi->curr_dma_words * 4;
/* Set attention level based on length of transfer */
- if (len & 0xF)
+ if (len & 0xF) {
val |= SPI_TX_TRIG_1 | SPI_RX_TRIG_1;
- else if (((len) >> 4) & 0x1)
+ dma_burst = 1;
+ } else if (((len) >> 4) & 0x1) {
val |= SPI_TX_TRIG_4 | SPI_RX_TRIG_4;
- else
+ dma_burst = 4;
+ } else {
val |= SPI_TX_TRIG_8 | SPI_RX_TRIG_8;
+ dma_burst = 8;
+ }
if (tspi->cur_direction & DATA_DIR_TX)
val |= SPI_IE_TX;
@@ -557,7 +563,18 @@ static int tegra_spi_start_dma_based_transfer(
tegra_spi_writel(tspi, val, SPI_DMA_CTL);
tspi->dma_control_reg = val;
+ dma_sconfig.device_fc = true;
if (tspi->cur_direction & DATA_DIR_TX) {
+ dma_sconfig.dst_addr = tspi->phys + SPI_TX_FIFO;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dma_sconfig.dst_maxburst = dma_burst;
+ ret = dmaengine_slave_config(tspi->tx_dma_chan, &dma_sconfig);
+ if (ret < 0) {
+ dev_err(tspi->dev,
+ "DMA slave config failed: %d\n", ret);
+ return ret;
+ }
+
tegra_spi_copy_client_txbuf_to_spi_txbuf(tspi, t);
ret = tegra_spi_start_tx_dma(tspi, len);
if (ret < 0) {
@@ -568,6 +585,16 @@ static int tegra_spi_start_dma_based_transfer(
}
if (tspi->cur_direction & DATA_DIR_RX) {
+ dma_sconfig.src_addr = tspi->phys + SPI_RX_FIFO;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dma_sconfig.src_maxburst = dma_burst;
+ ret = dmaengine_slave_config(tspi->rx_dma_chan, &dma_sconfig);
+ if (ret < 0) {
+ dev_err(tspi->dev,
+ "DMA slave config failed: %d\n", ret);
+ return ret;
+ }
+
/* Make the dma buffer to read by dma */
dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys,
tspi->dma_buf_size, DMA_FROM_DEVICE);
@@ -628,7 +655,6 @@ static int tegra_spi_init_dma_param(struct tegra_spi_data *tspi,
u32 *dma_buf;
dma_addr_t dma_phys;
int ret;
- struct dma_slave_config dma_sconfig;
dma_chan = dma_request_slave_channel_reason(tspi->dev,
dma_to_memory ? "rx" : "tx");
@@ -649,19 +675,6 @@ static int tegra_spi_init_dma_param(struct tegra_spi_data *tspi,
}
if (dma_to_memory) {
- dma_sconfig.src_addr = tspi->phys + SPI_RX_FIFO;
- dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- dma_sconfig.src_maxburst = 0;
- } else {
- dma_sconfig.dst_addr = tspi->phys + SPI_TX_FIFO;
- dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- dma_sconfig.dst_maxburst = 0;
- }
-
- ret = dmaengine_slave_config(dma_chan, &dma_sconfig);
- if (ret)
- goto scrub;
- if (dma_to_memory) {
tspi->rx_dma_chan = dma_chan;
tspi->rx_dma_buf = dma_buf;
tspi->rx_dma_phys = dma_phys;
@@ -671,11 +684,6 @@ static int tegra_spi_init_dma_param(struct tegra_spi_data *tspi,
tspi->tx_dma_phys = dma_phys;
}
return 0;
-
-scrub:
- dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys);
- dma_release_channel(dma_chan);
- return ret;
}
static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi,
--
2.7.4
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 <[email protected]>
---
drivers/spi/spi-tegra114.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 0e228f1e910e..d39f7b05b824 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -750,6 +750,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);
@@ -1137,7 +1142,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;
--
2.7.4
Fixes: use unpacked mode when transfer length is less than 4 bytes.
Packed mode expects minimum transfer length of 4 bytes.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index dc63536dbda4..716587b663a3 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 {
--
2.7.4
This patch dumps SPI registers on DMA or transfer timeout for debug
purpose.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index b7e71676a506..268a790a663e 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -858,6 +858,20 @@ static void tegra_spi_transfer_delay(int delay)
udelay(delay % 1000);
}
+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)
{
@@ -904,6 +918,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);
@@ -913,6 +928,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");
+ tegra_spi_dump_regs(tspi);
ret = -EIO;
goto complete_xfer;
}
@@ -958,6 +974,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);
reset_control_assert(tspi->rst);
udelay(2);
@@ -1031,6 +1048,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);
reset_control_assert(tspi->rst);
udelay(2);
--
2.7.4
Fixes: computation of actual bytes to fill/receive in/from FIFO in unpacked
mode when transfer length is not a multiple of requested bits per word.
unpacked mode transfers fails when the transfer includes partial bytes in
the last word.
Total words to be written/read to/from FIFO is computed based on transfer
length and bits per word. Unpacked mode includes 0 padding bytes for partial
words to align with bits per word and these extra bytes are also accounted
for calculating bytes left to transfer in the current driver.
This causes extra bytes access of tx/rx buffers along with buffer index
position crossing actual length where remain_len becomes negative and due to
unsigned type, negative value is a 32 bit representation of signed value
and transferred bytes never meets the actual transfer length resulting in
transfer timeout and a hang.
This patch fixes this with proper computation of the actual bytes to fill in
FIFO during transmit and the actual bytes to read from FIFO during receive
ignoring 0 padded bytes.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 43 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 36 insertions(+), 7 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 82431115b7c0..fdba302eb3b6 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -307,10 +307,16 @@ static unsigned tegra_spi_fill_tx_fifo_from_client_txbuf(
x |= (u32)(*tx_buf++) << (i * 8);
tegra_spi_writel(tspi, x, SPI_TX_FIFO);
}
+
+ tspi->cur_tx_pos += written_words * tspi->bytes_per_word;
} else {
+ unsigned int write_bytes;
max_n_32bit = min(tspi->curr_dma_words, tx_empty_count);
written_words = max_n_32bit;
nbytes = written_words * tspi->bytes_per_word;
+ if (nbytes > t->len - tspi->cur_pos)
+ nbytes = t->len - tspi->cur_pos;
+ write_bytes = nbytes;
for (count = 0; count < max_n_32bit; count++) {
u32 x = 0;
@@ -319,8 +325,10 @@ static unsigned tegra_spi_fill_tx_fifo_from_client_txbuf(
x |= (u32)(*tx_buf++) << (i * 8);
tegra_spi_writel(tspi, x, SPI_TX_FIFO);
}
+
+ tspi->cur_tx_pos += write_bytes;
}
- tspi->cur_tx_pos += written_words * tspi->bytes_per_word;
+
return written_words;
}
@@ -344,20 +352,27 @@ static unsigned int tegra_spi_read_rx_fifo_to_client_rxbuf(
for (i = 0; len && (i < 4); i++, len--)
*rx_buf++ = (x >> i*8) & 0xFF;
}
- tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
read_words += tspi->curr_dma_words;
+ tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
} else {
u32 rx_mask = ((u32)1 << t->bits_per_word) - 1;
+ u8 bytes_per_word = tspi->bytes_per_word;
+ unsigned int read_bytes;
+ len = rx_full_count * bytes_per_word;
+ if (len > t->len - tspi->cur_pos)
+ len = t->len - tspi->cur_pos;
+ read_bytes = len;
for (count = 0; count < rx_full_count; count++) {
u32 x = tegra_spi_readl(tspi, SPI_RX_FIFO) & rx_mask;
- for (i = 0; (i < tspi->bytes_per_word); i++)
+ for (i = 0; len && (i < bytes_per_word); i++, len--)
*rx_buf++ = (x >> (i*8)) & 0xFF;
}
- tspi->cur_rx_pos += rx_full_count * tspi->bytes_per_word;
read_words += rx_full_count;
+ tspi->cur_rx_pos += read_bytes;
}
+
return read_words;
}
@@ -372,12 +387,17 @@ static void tegra_spi_copy_client_txbuf_to_spi_txbuf(
unsigned len = tspi->curr_dma_words * tspi->bytes_per_word;
memcpy(tspi->tx_dma_buf, t->tx_buf + tspi->cur_pos, len);
+ tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
} else {
unsigned int i;
unsigned int count;
u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos;
unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word;
+ unsigned int write_bytes;
+ if (consume > t->len - tspi->cur_pos)
+ consume = t->len - tspi->cur_pos;
+ write_bytes = consume;
for (count = 0; count < tspi->curr_dma_words; count++) {
u32 x = 0;
@@ -386,8 +406,9 @@ static void tegra_spi_copy_client_txbuf_to_spi_txbuf(
x |= (u32)(*tx_buf++) << (i * 8);
tspi->tx_dma_buf[count] = x;
}
+
+ tspi->cur_tx_pos += write_bytes;
}
- tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
/* Make the dma buffer to read by dma */
dma_sync_single_for_device(tspi->dev, tspi->tx_dma_phys,
@@ -405,20 +426,28 @@ static void tegra_spi_copy_spi_rxbuf_to_client_rxbuf(
unsigned len = tspi->curr_dma_words * tspi->bytes_per_word;
memcpy(t->rx_buf + tspi->cur_rx_pos, tspi->rx_dma_buf, len);
+ tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
} else {
unsigned int i;
unsigned int count;
unsigned char *rx_buf = t->rx_buf + tspi->cur_rx_pos;
u32 rx_mask = ((u32)1 << t->bits_per_word) - 1;
+ unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word;
+ unsigned int read_bytes;
+ if (consume > t->len - tspi->cur_pos)
+ consume = t->len - tspi->cur_pos;
+ read_bytes = consume;
for (count = 0; count < tspi->curr_dma_words; count++) {
u32 x = tspi->rx_dma_buf[count] & rx_mask;
- for (i = 0; (i < tspi->bytes_per_word); i++)
+ for (i = 0; consume && (i < tspi->bytes_per_word);
+ i++, consume--)
*rx_buf++ = (x >> (i*8)) & 0xFF;
}
+
+ tspi->cur_rx_pos += read_bytes;
}
- tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
/* Make the dma buffer to read by dma */
dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys,
--
2.7.4
Fixes: move SPI controller reset out of spin lock.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 268a790a663e..5523936b21f5 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -976,11 +976,12 @@ static irqreturn_t handle_cpu_based_xfer(struct tegra_spi_data *tspi)
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);
reset_control_assert(tspi->rst);
udelay(2);
reset_control_deassert(tspi->rst);
- complete(&tspi->xfer_completion);
- goto exit;
+ return IRQ_HANDLED;
}
if (tspi->cur_direction & DATA_DIR_RX)
@@ -1050,11 +1051,11 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_spi_data *tspi)
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);
reset_control_assert(tspi->rst);
udelay(2);
reset_control_deassert(tspi->rst);
- complete(&tspi->xfer_completion);
- spin_unlock_irqrestore(&tspi->lock, flags);
return IRQ_HANDLED;
}
--
2.7.4
Fixes: Flush TX and RX FIFOs before start of new transfer and on FIFO
overflow or underrun errors.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 39 ++++++++++++++++++++++++++++++---------
1 file changed, 30 insertions(+), 9 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 0d20fc489134..477828051aba 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -500,22 +500,37 @@ static int tegra_spi_start_rx_dma(struct tegra_spi_data *tspi, int len)
return 0;
}
-static int tegra_spi_start_dma_based_transfer(
- struct tegra_spi_data *tspi, struct spi_transfer *t)
+static int tegra_spi_flush_fifos(struct tegra_spi_data *tspi)
{
- u32 val;
- unsigned int len;
- int ret = 0;
+ unsigned long timeout = jiffies + HZ;
u32 status;
- /* Make sure that Rx and Tx fifo are empty */
status = tegra_spi_readl(tspi, SPI_FIFO_STATUS);
if ((status & SPI_FIFO_EMPTY) != SPI_FIFO_EMPTY) {
- dev_err(tspi->dev, "Rx/Tx fifo are not empty status 0x%08x\n",
- (unsigned)status);
- return -EIO;
+ status |= SPI_RX_FIFO_FLUSH | SPI_TX_FIFO_FLUSH;
+ tegra_spi_writel(tspi, status, SPI_FIFO_STATUS);
+ while ((status & SPI_FIFO_EMPTY) != SPI_FIFO_EMPTY) {
+ status = tegra_spi_readl(tspi, SPI_FIFO_STATUS);
+ if (time_after(jiffies, timeout)) {
+ dev_err(tspi->dev,
+ "timeout waiting for fifo flush\n");
+ return -EIO;
+ }
+
+ udelay(1);
+ }
}
+ return 0;
+}
+
+static int tegra_spi_start_dma_based_transfer(
+ struct tegra_spi_data *tspi, struct spi_transfer *t)
+{
+ u32 val;
+ unsigned int len;
+ int ret = 0;
+
val = SPI_DMA_BLK_SET(tspi->curr_dma_words - 1);
tegra_spi_writel(tspi, val, SPI_DMA_BLK);
@@ -781,6 +796,9 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi,
dev_dbg(tspi->dev, "The def 0x%x and written 0x%x\n",
tspi->def_command1_reg, (unsigned)command1);
+ ret = tegra_spi_flush_fifos(tspi);
+ if (ret < 0)
+ return ret;
if (total_fifo_words > SPI_FIFO_DEPTH)
ret = tegra_spi_start_dma_based_transfer(tspi, t);
else
@@ -878,6 +896,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_flush_fifos(tspi);
reset_control_assert(tspi->rst);
udelay(2);
reset_control_deassert(tspi->rst);
@@ -931,6 +950,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_flush_fifos(tspi);
reset_control_assert(tspi->rst);
udelay(2);
reset_control_deassert(tspi->rst);
@@ -1003,6 +1023,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_flush_fifos(tspi);
reset_control_assert(tspi->rst);
udelay(2);
reset_control_deassert(tspi->rst);
--
2.7.4
This patch adds below cs timing properties to allow SPI master configuring
setup, hold and time interval between two SPI transactions to meet specific
SPI client device requirements.
CS setup time
CS hold time
CS inactive delay
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1;
t=1553666253; bh=TbJVgRAIkjok3TkKNkfcvYc4WGyZhOpiH6sUZvadGrQ=;
h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer:
In-Reply-To:References:X-NVConfidentiality:MIME-Version:
Content-Type;
b=Tud9guY5XCzkjKA2k2n0+xO/vTfMTNhH1e0viAyYmlgoG/j+uSBAAmOeZucVUlmEa
rwqxlSm+rkYMUfgSWqmJXqEjgRIAqMhA32FOJmhgpblGwYUR22hnbdhbk4TcqMNsIO
mXBgiIq3sOB7dKCDIv4+YSUreRrnXrvrDN0NE9zN7D+AQWYLF5dI4v+UDalNYJU2hz
TusNrURxuHgtpModvYVQRPgPDv+nJ2QE6L7yZH/M6XP7jYprtQHWnHAiMwqqSXBcrf
8vlhV96anQSaGkioOUTp8ThlHSwBKLRFHRofVlrhT53K0xvTCvVst4aHvA1aq3QPw0
sVr25yohIPq7w==
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
index 9ba7c5a273b4..6167c5234b64 100644
--- a/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
+++ b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
@@ -23,6 +23,13 @@ Required properties:
Recommended properties:
- spi-max-frequency: Definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
+
+spi-client device controller properties:
+- nvidia,cs-setup-clk-count: CS setup timing parameter.
+- nvidia,cs-hold-clk-count: CS hold timing parameter.
+- nvidia,cs-inactive-cycles: CS inactive delay in terms of clock between
+ transfers.
+
Example:
spi@7000d600 {
--
2.7.4
Fixes: SPI driver can be built as module so perform SPI controller reset
on probe to make sure it is in valid state before initiating transfer.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/spi/spi-tegra114.c | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 5523936b21f5..0e228f1e910e 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1157,27 +1157,19 @@ static int tegra_spi_probe(struct platform_device *pdev)
spi_irq = platform_get_irq(pdev, 0);
tspi->irq = spi_irq;
- ret = request_threaded_irq(tspi->irq, tegra_spi_isr,
- tegra_spi_isr_thread, IRQF_ONESHOT,
- dev_name(&pdev->dev), tspi);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
- tspi->irq);
- goto exit_free_master;
- }
tspi->clk = devm_clk_get(&pdev->dev, "spi");
if (IS_ERR(tspi->clk)) {
dev_err(&pdev->dev, "can not get clock\n");
ret = PTR_ERR(tspi->clk);
- goto exit_free_irq;
+ goto exit_free_master;
}
tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi");
if (IS_ERR(tspi->rst)) {
dev_err(&pdev->dev, "can not get reset\n");
ret = PTR_ERR(tspi->rst);
- goto exit_free_irq;
+ goto exit_free_master;
}
tspi->max_buf_size = SPI_FIFO_DEPTH << 2;
@@ -1185,7 +1177,7 @@ static int tegra_spi_probe(struct platform_device *pdev)
ret = tegra_spi_init_dma_param(tspi, true);
if (ret < 0)
- goto exit_free_irq;
+ goto exit_free_master;
ret = tegra_spi_init_dma_param(tspi, false);
if (ret < 0)
goto exit_rx_dma_free;
@@ -1207,18 +1199,32 @@ static int tegra_spi_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
goto exit_pm_disable;
}
+
+ reset_control_assert(tspi->rst);
+ udelay(2);
+ reset_control_deassert(tspi->rst);
tspi->def_command1_reg = SPI_M_S;
tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1);
pm_runtime_put(&pdev->dev);
+ ret = request_threaded_irq(tspi->irq, tegra_spi_isr,
+ tegra_spi_isr_thread, IRQF_ONESHOT,
+ dev_name(&pdev->dev), tspi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
+ tspi->irq);
+ goto exit_pm_disable;
+ }
master->dev.of_node = pdev->dev.of_node;
ret = devm_spi_register_master(&pdev->dev, master);
if (ret < 0) {
dev_err(&pdev->dev, "can not register to master err %d\n", ret);
- goto exit_pm_disable;
+ goto exit_free_irq;
}
return ret;
+exit_free_irq:
+ free_irq(spi_irq, tspi);
exit_pm_disable:
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
@@ -1226,8 +1232,6 @@ static int tegra_spi_probe(struct platform_device *pdev)
tegra_spi_deinit_dma_param(tspi, false);
exit_rx_dma_free:
tegra_spi_deinit_dma_param(tspi, true);
-exit_free_irq:
- free_irq(spi_irq, tspi);
exit_free_master:
spi_master_put(master);
return ret;
--
2.7.4
On Tue, Mar 26, 2019 at 10:56:42PM -0700, Sowjanya Komatineni wrote:
> This patch adds Tegra SPI master tx and rx clock delay properties.
>
> TX/RX clock delays may vary depending on the platform design trace lengths
> for each client on the Tegra SPI bus. These properties helps to tune the
> clock delays.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> .../devicetree/bindings/spi/nvidia,tegra114-spi.txt | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
Just combine this with patch 19.
> diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
> index 6167c5234b64..2b84b7b726ce 100644
> --- a/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
> +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
> @@ -29,6 +29,12 @@ spi-client device controller properties:
> - nvidia,cs-hold-clk-count: CS hold timing parameter.
> - nvidia,cs-inactive-cycles: CS inactive delay in terms of clock between
> transfers.
> +- nvidia,tx-clk-tap-delay: Delays the clock going out to the external device
> + with this tap value. This property is used to tune the outgoing data from
> + Tegra SPI master with respect to outgoing Tegra SPI master clock.
> +- nvidia,rx-clk-tap-delay: Delays the clock coming in from the external device
> + with this tap value. This property is used to adjust the Tegra SPI master
> + clock with respect to the data from the SPI slave device.
Are there units? What's the range of values.
>
> Example:
>
> @@ -45,4 +51,14 @@ spi@7000d600 {
> reset-names = "spi";
> dmas = <&apbdma 16>, <&apbdma 16>;
> dma-names = "rx", "tx";
> +
> + <spi-client>@<bus_num> {
> + ...
> + ...
> + nvidia,cs-setup-clk-count = <10>;
> + nvidia,cs-hold-clk-count = <10>;
> + nvidia,rx-clk-tap-delay = <0>;
> + nvidia,tx-clk-tap-delay = <16>;
> + ...
> + };
> };
> --
> 2.7.4
>
On Tue, Mar 26, 2019 at 10:56:40PM -0700, Sowjanya Komatineni wrote:
> This patch adds below cs timing properties to allow SPI master configuring
> setup, hold and time interval between two SPI transactions to meet specific
> SPI client device requirements.
> CS setup time
> CS hold time
> CS inactive delay
> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1;
> t=1553666253; bh=TbJVgRAIkjok3TkKNkfcvYc4WGyZhOpiH6sUZvadGrQ=;
> h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer:
> In-Reply-To:References:X-NVConfidentiality:MIME-Version:
> Content-Type;
> b=Tud9guY5XCzkjKA2k2n0+xO/vTfMTNhH1e0viAyYmlgoG/j+uSBAAmOeZucVUlmEa
> rwqxlSm+rkYMUfgSWqmJXqEjgRIAqMhA32FOJmhgpblGwYUR22hnbdhbk4TcqMNsIO
Something got corrupted?
> +spi-client device controller properties:
> +- nvidia,cs-setup-clk-count: CS setup timing parameter.
> +- nvidia,cs-hold-clk-count: CS hold timing parameter.
> +- nvidia,cs-inactive-cycles: CS inactive delay in terms of clock between
> + transfers.
Why are these being done as nVidia specific properties rather than
generic ones and why are these being configured in DT rather than by the
client driver? If the devices have particular timing requirements for
chip select presumably that's going to apply no matter what controller
or system they're used with so it seems best to configure this in the
client driver and have an API that any controller can implement.
On Tue, Mar 26, 2019 at 10:56:25PM -0700, Sowjanya Komatineni wrote:
> Fixes: Use packed mode for 32 bits per word transfers to increase
> performance as each packet is a full 32-bit word.
This is an optimization rather than a bug fix, ideally it should be
later in the series to make things easier to apply as fixes for Linus.
Also the way you're writing your commit logs with "Fixes: " at the start
is really weird and conflicts a bit with the Fixes: tag that the kernel
uses to reference the commit that introduces bugs.
On Tue, Mar 26, 2019 at 10:56:30PM -0700, Sowjanya Komatineni wrote:
> This patch dumps SPI registers on DMA or transfer timeout for debug
> purpose.
This is another non-bugfix patch which should be after the bugfixes.
On Tue, Mar 26, 2019 at 10:56:39PM -0700, Sowjanya Komatineni wrote:
> Tegra SPI driver default uses SW CS control for transfers and HW CS
> control can be enabled through SPI client device node DT property
> nvidia,enable-hw-based-cs and is used only for single transfers.
Why have a property - if this feature works and is useful why would a
system ever not want to use it?
> + single_xfer = list_is_singular(&msg->transfers);
> list_for_each_entry(xfer, &msg->transfers, transfer_list) {
> u32 cmd1;
>
> reinit_completion(&tspi->xfer_completion);
>
> - cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg);
> + cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg,
> + single_xfer);
It's not sufficient to check if there's only one transfer, you also need
to make sure that the driver isn't using cs_change to use non-standard
chip select handling.
On Tue, Mar 26, 2019 at 10:56:45PM -0700, Sowjanya Komatineni wrote:
> With SW CS, during transfer completion CS is de-asserted by writing the
> default command1 register value to SPI_COMMAND1 register. With this both
> mode and CS state are set at the same time and if current transfer mode
> is different to default SPI mode and if mode change happens prior to CS
> de-assert, clock polarity can change while CS is active before transfer
> finishes.
This is a bug fix so I'd expect it to be much earlier in the series
before any of the new features.
On Tue, Mar 26, 2019 at 10:56:26PM -0700, Sowjanya Komatineni wrote:
> Fixes: use unpacked mode when transfer length is less than 4 bytes.
>
> Packed mode expects minimum transfer length of 4 bytes.
This doesn't apply against current code, please check and resend.
Applying: spi: tegra114: use unpacked mode for below 4 byte transfers
error: sha1 information is lacking or useless (drivers/spi/spi-tegra114.c).
error: could not build fake ancestor
The patch
spi: tegra114: add SPI_LSB_FIRST support
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From 2b17a3c759e5e2ed3faafb69e243ec312e3bf0da Mon Sep 17 00:00:00 2001
From: Sowjanya Komatineni <[email protected]>
Date: Tue, 26 Mar 2019 22:56:33 -0700
Subject: [PATCH] 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 <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-tegra114.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
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;
--
2.20.1
The patch
spi: tegra114: reset controller on probe
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From 019194933339b3e9b486639c8cb3692020844d65 Mon Sep 17 00:00:00 2001
From: Sowjanya Komatineni <[email protected]>
Date: Tue, 26 Mar 2019 22:56:32 -0700
Subject: [PATCH] spi: tegra114: reset controller on probe
Fixes: SPI driver can be built as module so perform SPI controller reset
on probe to make sure it is in valid state before initiating transfer.
Signed-off-by: Sowjanya Komatineni <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-tegra114.c | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 05bb2f9bff3c..09cfae3abce2 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1136,27 +1136,19 @@ static int tegra_spi_probe(struct platform_device *pdev)
spi_irq = platform_get_irq(pdev, 0);
tspi->irq = spi_irq;
- ret = request_threaded_irq(tspi->irq, tegra_spi_isr,
- tegra_spi_isr_thread, IRQF_ONESHOT,
- dev_name(&pdev->dev), tspi);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
- tspi->irq);
- goto exit_free_master;
- }
tspi->clk = devm_clk_get(&pdev->dev, "spi");
if (IS_ERR(tspi->clk)) {
dev_err(&pdev->dev, "can not get clock\n");
ret = PTR_ERR(tspi->clk);
- goto exit_free_irq;
+ goto exit_free_master;
}
tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi");
if (IS_ERR(tspi->rst)) {
dev_err(&pdev->dev, "can not get reset\n");
ret = PTR_ERR(tspi->rst);
- goto exit_free_irq;
+ goto exit_free_master;
}
tspi->max_buf_size = SPI_FIFO_DEPTH << 2;
@@ -1164,7 +1156,7 @@ static int tegra_spi_probe(struct platform_device *pdev)
ret = tegra_spi_init_dma_param(tspi, true);
if (ret < 0)
- goto exit_free_irq;
+ goto exit_free_master;
ret = tegra_spi_init_dma_param(tspi, false);
if (ret < 0)
goto exit_rx_dma_free;
@@ -1186,18 +1178,32 @@ static int tegra_spi_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
goto exit_pm_disable;
}
+
+ reset_control_assert(tspi->rst);
+ udelay(2);
+ reset_control_deassert(tspi->rst);
tspi->def_command1_reg = SPI_M_S;
tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1);
pm_runtime_put(&pdev->dev);
+ ret = request_threaded_irq(tspi->irq, tegra_spi_isr,
+ tegra_spi_isr_thread, IRQF_ONESHOT,
+ dev_name(&pdev->dev), tspi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
+ tspi->irq);
+ goto exit_pm_disable;
+ }
master->dev.of_node = pdev->dev.of_node;
ret = devm_spi_register_master(&pdev->dev, master);
if (ret < 0) {
dev_err(&pdev->dev, "can not register to master err %d\n", ret);
- goto exit_pm_disable;
+ goto exit_free_irq;
}
return ret;
+exit_free_irq:
+ free_irq(spi_irq, tspi);
exit_pm_disable:
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
@@ -1205,8 +1211,6 @@ static int tegra_spi_probe(struct platform_device *pdev)
tegra_spi_deinit_dma_param(tspi, false);
exit_rx_dma_free:
tegra_spi_deinit_dma_param(tspi, true);
-exit_free_irq:
- free_irq(spi_irq, tspi);
exit_free_master:
spi_master_put(master);
return ret;
--
2.20.1
The patch
spi: tegra114: flush fifos
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From c4fc9e5b28ff787e35137c2cc13316bb11d7657b Mon Sep 17 00:00:00 2001
From: Sowjanya Komatineni <[email protected]>
Date: Tue, 26 Mar 2019 22:56:28 -0700
Subject: [PATCH] spi: tegra114: flush fifos
Fixes: Flush TX and RX FIFOs before start of new transfer and on FIFO
overflow or underrun errors.
Signed-off-by: Sowjanya Komatineni <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-tegra114.c | 39 +++++++++++++++++++++++++++++---------
1 file changed, 30 insertions(+), 9 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index a6153b905d1a..28aa080a94ff 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -499,22 +499,37 @@ static int tegra_spi_start_rx_dma(struct tegra_spi_data *tspi, int len)
return 0;
}
-static int tegra_spi_start_dma_based_transfer(
- struct tegra_spi_data *tspi, struct spi_transfer *t)
+static int tegra_spi_flush_fifos(struct tegra_spi_data *tspi)
{
- u32 val;
- unsigned int len;
- int ret = 0;
+ unsigned long timeout = jiffies + HZ;
u32 status;
- /* Make sure that Rx and Tx fifo are empty */
status = tegra_spi_readl(tspi, SPI_FIFO_STATUS);
if ((status & SPI_FIFO_EMPTY) != SPI_FIFO_EMPTY) {
- dev_err(tspi->dev, "Rx/Tx fifo are not empty status 0x%08x\n",
- (unsigned)status);
- return -EIO;
+ status |= SPI_RX_FIFO_FLUSH | SPI_TX_FIFO_FLUSH;
+ tegra_spi_writel(tspi, status, SPI_FIFO_STATUS);
+ while ((status & SPI_FIFO_EMPTY) != SPI_FIFO_EMPTY) {
+ status = tegra_spi_readl(tspi, SPI_FIFO_STATUS);
+ if (time_after(jiffies, timeout)) {
+ dev_err(tspi->dev,
+ "timeout waiting for fifo flush\n");
+ return -EIO;
+ }
+
+ udelay(1);
+ }
}
+ return 0;
+}
+
+static int tegra_spi_start_dma_based_transfer(
+ struct tegra_spi_data *tspi, struct spi_transfer *t)
+{
+ u32 val;
+ unsigned int len;
+ int ret = 0;
+
val = SPI_DMA_BLK_SET(tspi->curr_dma_words - 1);
tegra_spi_writel(tspi, val, SPI_DMA_BLK);
@@ -779,6 +794,9 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi,
dev_dbg(tspi->dev, "The def 0x%x and written 0x%x\n",
tspi->def_command1_reg, (unsigned)command1);
+ ret = tegra_spi_flush_fifos(tspi);
+ if (ret < 0)
+ return ret;
if (total_fifo_words > SPI_FIFO_DEPTH)
ret = tegra_spi_start_dma_based_transfer(tspi, t);
else
@@ -876,6 +894,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_flush_fifos(tspi);
reset_control_assert(tspi->rst);
udelay(2);
reset_control_deassert(tspi->rst);
@@ -929,6 +948,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_flush_fifos(tspi);
reset_control_assert(tspi->rst);
udelay(2);
reset_control_deassert(tspi->rst);
@@ -1001,6 +1021,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_flush_fifos(tspi);
reset_control_assert(tspi->rst);
udelay(2);
reset_control_deassert(tspi->rst);
--
2.20.1
The patch
spi: tegra114: terminate dma and reset on transfer timeout
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From 32bd1a9551cae34e6889afa235c7afdfede9aeac Mon Sep 17 00:00:00 2001
From: Sowjanya Komatineni <[email protected]>
Date: Tue, 26 Mar 2019 22:56:27 -0700
Subject: [PATCH] spi: tegra114: terminate dma and reset on transfer timeout
Fixes: terminate DMA and perform controller reset on transfer timeout
to clear the FIFO's and errors.
Signed-off-by: Sowjanya Komatineni <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-tegra114.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 876eb2acdef1..a6153b905d1a 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -869,7 +869,16 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
if (WARN_ON(ret == 0)) {
dev_err(tspi->dev,
"spi transfer timeout, err %d\n", ret);
+ if (tspi->is_curr_dma_xfer &&
+ (tspi->cur_direction & DATA_DIR_TX))
+ dmaengine_terminate_all(tspi->tx_dma_chan);
+ if (tspi->is_curr_dma_xfer &&
+ (tspi->cur_direction & DATA_DIR_RX))
+ dmaengine_terminate_all(tspi->rx_dma_chan);
ret = -EIO;
+ reset_control_assert(tspi->rst);
+ udelay(2);
+ reset_control_deassert(tspi->rst);
goto complete_xfer;
}
--
2.20.1
The patch
spi: tegra114: clear packed bit for unpacked mode
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From 7b3d10cdf54b8bc1dc0da21faed9789ac4da3684 Mon Sep 17 00:00:00 2001
From: Sowjanya Komatineni <[email protected]>
Date: Tue, 26 Mar 2019 22:56:23 -0700
Subject: [PATCH] spi: tegra114: clear packed bit for unpacked mode
Fixes: Clear packed bit when not using packed mode.
Packed bit is not cleared when not using packed mode. This results
in transfer timeouts for the unpacked mode transfers followed by the
packed mode transfers.
Signed-off-by: Sowjanya Komatineni <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-tegra114.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index a76acedd7e2f..1435792944c4 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -730,6 +730,8 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi,
if (tspi->is_packed)
command1 |= SPI_PACKED;
+ else
+ command1 &= ~SPI_PACKED;
command1 &= ~(SPI_CS_SEL_MASK | SPI_TX_EN | SPI_RX_EN);
tspi->cur_direction = 0;
--
2.20.1
The patch
spi: tegra114: use packed mode for 32 bits per word
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From fc9ba6e3e28afcedf3bb3105b1d8cbc5c183c2a3 Mon Sep 17 00:00:00 2001
From: Sowjanya Komatineni <[email protected]>
Date: Tue, 26 Mar 2019 22:56:25 -0700
Subject: [PATCH] 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 <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-tegra114.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 {
--
2.20.1
The patch
spi: tegra114: fix for unpacked mode transfers
has been applied to the spi tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
From 1a89ac5b91895127f7c586ec5075c3753ca25501 Mon Sep 17 00:00:00 2001
From: Sowjanya Komatineni <[email protected]>
Date: Tue, 26 Mar 2019 22:56:24 -0700
Subject: [PATCH] spi: tegra114: fix for unpacked mode transfers
Fixes: computation of actual bytes to fill/receive in/from FIFO in unpacked
mode when transfer length is not a multiple of requested bits per word.
unpacked mode transfers fails when the transfer includes partial bytes in
the last word.
Total words to be written/read to/from FIFO is computed based on transfer
length and bits per word. Unpacked mode includes 0 padding bytes for partial
words to align with bits per word and these extra bytes are also accounted
for calculating bytes left to transfer in the current driver.
This causes extra bytes access of tx/rx buffers along with buffer index
position crossing actual length where remain_len becomes negative and due to
unsigned type, negative value is a 32 bit representation of signed value
and transferred bytes never meets the actual transfer length resulting in
transfer timeout and a hang.
This patch fixes this with proper computation of the actual bytes to fill in
FIFO during transmit and the actual bytes to read from FIFO during receive
ignoring 0 padded bytes.
Signed-off-by: Sowjanya Komatineni <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-tegra114.c | 43 +++++++++++++++++++++++++++++++-------
1 file changed, 36 insertions(+), 7 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 1435792944c4..876eb2acdef1 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -307,10 +307,16 @@ static unsigned tegra_spi_fill_tx_fifo_from_client_txbuf(
x |= (u32)(*tx_buf++) << (i * 8);
tegra_spi_writel(tspi, x, SPI_TX_FIFO);
}
+
+ tspi->cur_tx_pos += written_words * tspi->bytes_per_word;
} else {
+ unsigned int write_bytes;
max_n_32bit = min(tspi->curr_dma_words, tx_empty_count);
written_words = max_n_32bit;
nbytes = written_words * tspi->bytes_per_word;
+ if (nbytes > t->len - tspi->cur_pos)
+ nbytes = t->len - tspi->cur_pos;
+ write_bytes = nbytes;
for (count = 0; count < max_n_32bit; count++) {
u32 x = 0;
@@ -319,8 +325,10 @@ static unsigned tegra_spi_fill_tx_fifo_from_client_txbuf(
x |= (u32)(*tx_buf++) << (i * 8);
tegra_spi_writel(tspi, x, SPI_TX_FIFO);
}
+
+ tspi->cur_tx_pos += write_bytes;
}
- tspi->cur_tx_pos += written_words * tspi->bytes_per_word;
+
return written_words;
}
@@ -344,20 +352,27 @@ static unsigned int tegra_spi_read_rx_fifo_to_client_rxbuf(
for (i = 0; len && (i < 4); i++, len--)
*rx_buf++ = (x >> i*8) & 0xFF;
}
- tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
read_words += tspi->curr_dma_words;
+ tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
} else {
u32 rx_mask = ((u32)1 << t->bits_per_word) - 1;
+ u8 bytes_per_word = tspi->bytes_per_word;
+ unsigned int read_bytes;
+ len = rx_full_count * bytes_per_word;
+ if (len > t->len - tspi->cur_pos)
+ len = t->len - tspi->cur_pos;
+ read_bytes = len;
for (count = 0; count < rx_full_count; count++) {
u32 x = tegra_spi_readl(tspi, SPI_RX_FIFO) & rx_mask;
- for (i = 0; (i < tspi->bytes_per_word); i++)
+ for (i = 0; len && (i < bytes_per_word); i++, len--)
*rx_buf++ = (x >> (i*8)) & 0xFF;
}
- tspi->cur_rx_pos += rx_full_count * tspi->bytes_per_word;
read_words += rx_full_count;
+ tspi->cur_rx_pos += read_bytes;
}
+
return read_words;
}
@@ -372,12 +387,17 @@ static void tegra_spi_copy_client_txbuf_to_spi_txbuf(
unsigned len = tspi->curr_dma_words * tspi->bytes_per_word;
memcpy(tspi->tx_dma_buf, t->tx_buf + tspi->cur_pos, len);
+ tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
} else {
unsigned int i;
unsigned int count;
u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos;
unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word;
+ unsigned int write_bytes;
+ if (consume > t->len - tspi->cur_pos)
+ consume = t->len - tspi->cur_pos;
+ write_bytes = consume;
for (count = 0; count < tspi->curr_dma_words; count++) {
u32 x = 0;
@@ -386,8 +406,9 @@ static void tegra_spi_copy_client_txbuf_to_spi_txbuf(
x |= (u32)(*tx_buf++) << (i * 8);
tspi->tx_dma_buf[count] = x;
}
+
+ tspi->cur_tx_pos += write_bytes;
}
- tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
/* Make the dma buffer to read by dma */
dma_sync_single_for_device(tspi->dev, tspi->tx_dma_phys,
@@ -405,20 +426,28 @@ static void tegra_spi_copy_spi_rxbuf_to_client_rxbuf(
unsigned len = tspi->curr_dma_words * tspi->bytes_per_word;
memcpy(t->rx_buf + tspi->cur_rx_pos, tspi->rx_dma_buf, len);
+ tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
} else {
unsigned int i;
unsigned int count;
unsigned char *rx_buf = t->rx_buf + tspi->cur_rx_pos;
u32 rx_mask = ((u32)1 << t->bits_per_word) - 1;
+ unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word;
+ unsigned int read_bytes;
+ if (consume > t->len - tspi->cur_pos)
+ consume = t->len - tspi->cur_pos;
+ read_bytes = consume;
for (count = 0; count < tspi->curr_dma_words; count++) {
u32 x = tspi->rx_dma_buf[count] & rx_mask;
- for (i = 0; (i < tspi->bytes_per_word); i++)
+ for (i = 0; consume && (i < tspi->bytes_per_word);
+ i++, consume--)
*rx_buf++ = (x >> (i*8)) & 0xFF;
}
+
+ tspi->cur_rx_pos += read_bytes;
}
- tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
/* Make the dma buffer to read by dma */
dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys,
--
2.20.1
> On Tue, Mar 26, 2019 at 10:56:40PM -0700, Sowjanya Komatineni wrote:
> > This patch adds below cs timing properties to allow SPI master
> > configuring setup, hold and time interval between two SPI transactions
> > to meet specific SPI client device requirements.
> > CS setup time
> > CS hold time
> > CS inactive delay
> > DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1;
> > t=1553666253; bh=TbJVgRAIkjok3TkKNkfcvYc4WGyZhOpiH6sUZvadGrQ=;
> > h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer:
> > In-Reply-To:References:X-NVConfidentiality:MIME-Version:
> > Content-Type;
> > b=Tud9guY5XCzkjKA2k2n0+xO/vTfMTNhH1e0viAyYmlgoG/j+uSBAAmOeZucVUlmEa
> > rwqxlSm+rkYMUfgSWqmJXqEjgRIAqMhA32FOJmhgpblGwYUR22hnbdhbk4TcqMNsIO
>
> Something got corrupted?
Possible. Will re-send anyway with your feedback fixes.
> > +spi-client device controller properties:
> > +- nvidia,cs-setup-clk-count: CS setup timing parameter.
> > +- nvidia,cs-hold-clk-count: CS hold timing parameter.
> > +- nvidia,cs-inactive-cycles: CS inactive delay in terms of clock
> > +between
> > + transfers.
>
> Why are these being done as nVidia specific properties rather than generic ones and why are these being configured in DT rather than by the client driver? If the devices have particular timing requirements for chip select presumably that's going to apply no matter what controller or system they're used with so it seems best to configure this in the client driver and have an API that any controller can implement.
These are implemented thru DT as Tegra SPI is master and master controls the timing.
Some SPI slaves have specific requirements of certain CS setup/hold time and inactive cycles which SPI master should meet when driving during transfer and Tegra SPI controller supports tuning these parameters.
> On Tue, Mar 26, 2019 at 10:56:45PM -0700, Sowjanya Komatineni wrote:
> > With SW CS, during transfer completion CS is de-asserted by writing
> > the default command1 register value to SPI_COMMAND1 register. With
> > this both mode and CS state are set at the same time and if current
> > transfer mode is different to default SPI mode and if mode change
> > happens prior to CS de-assert, clock polarity can change while CS is
> > active before transfer finishes.
>
> This is a bug fix so I'd expect it to be much earlier in the series before any of the new features.
Thanks Mark. Will change order as per your feedback in next version of patch series.
I see you have applied some patches in V1 series so should I re-send again those as well along with feedback changes in next version or just only the patches that are not applied.
> On Tue, Mar 26, 2019 at 10:56:25PM -0700, Sowjanya Komatineni wrote:
> > Fixes: Use packed mode for 32 bits per word transfers to increase
> > performance as each packet is a full 32-bit word.
>
> This is an optimization rather than a bug fix, ideally it should be later in the series to make things easier to apply as fixes for Linus.
>
> Also the way you're writing your commit logs with "Fixes: " at the start is really weird and conflicts a bit with the Fixes: tag that the kernel uses to reference the commit that introduces bugs.
Thanks Mark. Misunderstood use of Fixes tag.
Will remove Fixes tag in commit message in next version as fixes are not really for specific existing commit.
> On Tue, Mar 26, 2019 at 10:56:39PM -0700, Sowjanya Komatineni wrote:
>
> > Tegra SPI driver default uses SW CS control for transfers and HW CS
> > control can be enabled through SPI client device node DT property
> > nvidia,enable-hw-based-cs and is used only for single transfers.
>
> Why have a property - if this feature works and is useful why would a system ever not want to use it?
> > + single_xfer = list_is_singular(&msg->transfers);
> > list_for_each_entry(xfer, &msg->transfers, transfer_list) {
> > u32 cmd1;
> >
> > reinit_completion(&tspi->xfer_completion);
> >
> > - cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg);
> > + cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg,
> > + single_xfer);
>
> It's not sufficient to check if there's only one transfer, you also need to make sure that the driver isn't using cs_change to use non-standard chip select handling.
Yes we can avoid DT and force to use HW CS incase of all single transfers without cs_change. Will update in next version.
On Mon, Apr 01, 2019 at 05:59:57PM +0000, Sowjanya Komatineni wrote:
Please fix your mail client to word wrap within paragraphs at something
substantially less than 80 columns. Doing this makes your messages much
easier to read and reply to.
> > > +spi-client device controller properties:
> > > +- nvidia,cs-setup-clk-count: CS setup timing parameter.
> > > +- nvidia,cs-hold-clk-count: CS hold timing parameter.
> > > +- nvidia,cs-inactive-cycles: CS inactive delay in terms of clock
> > > +between
> > > + transfers.
> > Why are these being done as nVidia specific properties rather than
> > generic ones and why are these being configured in DT rather than by
> > the client driver? If the devices have particular timing
> > requirements for chip select presumably that's going to apply no
> > matter what controller or system they're used with so it seems best
> > to configure this in the client driver and have an API that any
> > controller can implement.
> These are implemented thru DT as Tegra SPI is master and master controls the timing.
> Some SPI slaves have specific requirements of certain CS setup/hold time and inactive cycles which SPI master should meet when driving during transfer and Tegra SPI controller supports tuning these parameters.
This doesn't address the issue at all, obviously the timings for the SPI
bus are going to be impelemented by the controller but that's not the
issue.
On Mon, Apr 01, 2019 at 06:07:45PM +0000, Sowjanya Komatineni wrote:
> I see you have applied some patches in V1 series so should I re-send
> again those as well along with feedback changes in next version or
> just only the patches that are not applied.
Please don't resend already applied patches.
> On Tue, Mar 26, 2019 at 10:56:42PM -0700, Sowjanya Komatineni wrote:
> > This patch adds Tegra SPI master tx and rx clock delay properties.
> >
> > TX/RX clock delays may vary depending on the platform design trace
> > lengths for each client on the Tegra SPI bus. These properties helps
> > to tune the clock delays.
> >
> > Signed-off-by: Sowjanya Komatineni <[email protected]>
> > ---
> > .../devicetree/bindings/spi/nvidia,tegra114-spi.txt | 16 ++++++++++++++++
> > 1 file changed, 16 insertions(+)
>
> Just combine this with patch 19.
>
> > diff --git
> > a/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
> > b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
> > index 6167c5234b64..2b84b7b726ce 100644
> > --- a/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
> > +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt
> > @@ -29,6 +29,12 @@ spi-client device controller properties:
> > - nvidia,cs-hold-clk-count: CS hold timing parameter.
> > - nvidia,cs-inactive-cycles: CS inactive delay in terms of clock between
> > transfers.
> > +- nvidia,tx-clk-tap-delay: Delays the clock going out to the external
> > +device
> > + with this tap value. This property is used to tune the outgoing
> > +data from
> > + Tegra SPI master with respect to outgoing Tegra SPI master clock.
> > +- nvidia,rx-clk-tap-delay: Delays the clock coming in from the
> > +external device
> > + with this tap value. This property is used to adjust the Tegra SPI
> > +master
> > + clock with respect to the data from the SPI slave device.
>
> Are there units? What's the range of values.
TX/RX Clock delays are tap counts and there is internal tap-to-tap delay.
Will update to specify tap to delay correlation along with supported tap values range.
As per feedback from mark, will move CS timing to API implementation as they are not
Tegra SPI specific and applicable in general.
>
> >
> > Example:
> >
> > @@ -45,4 +51,14 @@ spi@7000d600 {
> > reset-names = "spi";
> > dmas = <&apbdma 16>, <&apbdma 16>;
> > dma-names = "rx", "tx";
> > +
> > + <spi-client>@<bus_num> {
> > + ...
> > + ...
> > + nvidia,cs-setup-clk-count = <10>;
> > + nvidia,cs-hold-clk-count = <10>;
> > + nvidia,rx-clk-tap-delay = <0>;
> > + nvidia,tx-clk-tap-delay = <16>;
> > + ...
> > + };
> > };
> > --
> > 2.7.4
> >