From 63c1440596ff0a34efec491f41832c2c9ea01857 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 May 2019 22:03:52 -0700 Subject: spi: tegra114: add support for gpio based CS This patch adds support for GPIO based CS control through SPI core function spi_set_cs. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index b1f31bb16659..f47417dd9edb 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -776,6 +776,10 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, } else tegra_spi_writel(tspi, command1, SPI_COMMAND1); + /* GPIO based chip select control */ + if (spi->cs_gpiod) + gpiod_set_value(spi->cs_gpiod, 1); + command1 |= SPI_CS_SW_HW; if (spi->mode & SPI_CS_HIGH) command1 |= SPI_CS_SW_VAL; @@ -864,6 +868,10 @@ static int tegra_spi_setup(struct spi_device *spi) } spin_lock_irqsave(&tspi->lock, flags); + /* GPIO based chip select control */ + if (spi->cs_gpiod) + gpiod_set_value(spi->cs_gpiod, 0); + val = tspi->def_command1_reg; if (spi->mode & SPI_CS_HIGH) val &= ~SPI_CS_POL_INACTIVE(spi->chip_select); @@ -893,6 +901,10 @@ static void tegra_spi_transfer_end(struct spi_device *spi) struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); int cs_val = (spi->mode & SPI_CS_HIGH) ? 0 : 1; + /* GPIO based chip select control */ + if (spi->cs_gpiod) + gpiod_set_value(spi->cs_gpiod, 0); + if (cs_val) tspi->command1_reg |= SPI_CS_SW_VAL; else @@ -1199,6 +1211,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->use_gpio_descriptors = true; 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); -- cgit v1.2.3 From 1bf9f3c9232d08651aa6990001e7b042c95303dc Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 May 2019 22:03:53 -0700 Subject: spi: tegra114: add support for hw based cs Tegra SPI controller supports both HW and SW based CS control for SPI transfers. This patch adds support for HW based CS control where CS is driven to active state during the transfer and is driven inactive at the end of the transfer directly by the HW. This patch enables the use of HW based CS only for single transfers without cs_change request. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index f47417dd9edb..0cb0932d32fd 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -193,6 +193,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,7 +724,9 @@ 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); u32 speed = t->speed_hz; @@ -780,11 +783,17 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, if (spi->cs_gpiod) gpiod_set_value(spi->cs_gpiod, 1); - command1 |= SPI_CS_SW_HW; - if (spi->mode & SPI_CS_HIGH) - command1 |= SPI_CS_SW_VAL; - else - command1 &= ~SPI_CS_SW_VAL; + if (is_single_xfer && !(t->cs_change)) { + tspi->use_hw_based_cs = true; + command1 &= ~(SPI_CS_SW_HW | SPI_CS_SW_VAL); + } else { + tspi->use_hw_based_cs = false; + command1 |= SPI_CS_SW_HW; + if (spi->mode & SPI_CS_HIGH) + command1 |= SPI_CS_SW_VAL; + else + command1 &= ~SPI_CS_SW_VAL; + } tegra_spi_writel(tspi, 0, SPI_COMMAND2); } else { @@ -905,11 +914,14 @@ static void tegra_spi_transfer_end(struct spi_device *spi) if (spi->cs_gpiod) gpiod_set_value(spi->cs_gpiod, 0); - if (cs_val) - tspi->command1_reg |= SPI_CS_SW_VAL; - else - tspi->command1_reg &= ~SPI_CS_SW_VAL; - tegra_spi_writel(tspi, tspi->command1_reg, SPI_COMMAND1); + if (!tspi->use_hw_based_cs) { + if (cs_val) + 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); } @@ -936,16 +948,19 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, struct spi_device *spi = msg->spi; int ret; bool skip = false; + int single_xfer; 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; -- cgit v1.2.3 From 9b76ef39b7fbc2ddb0869725f9745a402d93cce5 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 May 2019 22:03:54 -0700 Subject: spi: tegra114: add support for HW CS timing This patch implements set_cs_timing SPI controller method to allow SPI client driver to configure device specific SPI CS timings. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 48 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 0cb0932d32fd..e59ff7c1cee6 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) @@ -206,6 +208,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; @@ -723,6 +727,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, u8 setup_dly, + u8 hold_dly, u8 inactive_dly) +{ + struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); + u32 setup_hold; + u32 spi_cs_timing; + u32 inactive_cycles; + u8 cs_state; + + setup_dly = min_t(u8, setup_dly, MAX_SETUP_HOLD_CYCLES); + hold_dly = min_t(u8, hold_dly, MAX_SETUP_HOLD_CYCLES); + if (setup_dly && hold_dly) { + 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); + } + } + + inactive_cycles = min_t(u8, inactive_dly, MAX_INACTIVE_CYCLES); + if (inactive_cycles) + inactive_cycles--; + cs_state = inactive_cycles ? 0 : 1; + spi_cs_timing = tspi->spi_cs_timing2; + 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, @@ -1232,6 +1273,7 @@ static int tegra_spi_probe(struct platform_device *pdev) 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->set_cs_timing = tegra_spi_set_hw_cs_timing; master->num_chipselect = MAX_CHIP_SELECT; master->auto_runtime_pm = true; bus_num = of_alias_get_id(pdev->dev.of_node, "spi"); @@ -1307,6 +1349,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, -- cgit v1.2.3 From 318dacbd049b447a5b45290b39f1c889b9cbde4d Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 May 2019 22:03:55 -0700 Subject: spi: tegra114: add support for TX and RX trimmers Tegra SPI master controller has programmable trimmers to adjust the data with respect to the clock. These trimmers are programmed in TX_CLK_TAP_DELAY and RX_CLK_TAP_DELAY fields of COMMAND2 register. SPI TX trimmer is to adjust the outgoing data with respect to the outgoing clock and SPI RX trimmer is to adjust the loopback clock with respect to the incoming data from the slave device. These trimmers vary based on trace lengths of the platform design for each of the slaves on the SPI bus and optimal value programmed is from the platform validation across PVT. This patch adds support for configuring TX and RX clock delay trimmers through the device tree properties. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 67 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index e59ff7c1cee6..253a7f182fc9 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -169,6 +169,11 @@ struct tegra_spi_soc_data { bool has_intr_mask_reg; }; +struct tegra_spi_client_data { + int tx_clk_tap_delay; + int rx_clk_tap_delay; +}; + struct tegra_spi_data { struct device *dev; struct spi_master *master; @@ -208,8 +213,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; @@ -770,10 +777,12 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, 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; + u32 command1, command2; int req_mode; + u32 tx_tap = 0, rx_tap = 0; if (speed != tspi->cur_speed) { clk_set_rate(tspi->clk, speed); @@ -836,7 +845,18 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, command1 &= ~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); @@ -892,9 +912,42 @@ 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; + + 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; +} + +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; @@ -905,6 +958,11 @@ 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); @@ -1034,6 +1092,7 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, reset_control_assert(tspi->rst); udelay(2); reset_control_deassert(tspi->rst); + tspi->last_used_cs = master->num_chipselect + 1; goto complete_xfer; } @@ -1351,6 +1410,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, @@ -1423,6 +1484,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); -- cgit v1.2.3 From 0e896f380b845d3fb889f27eb6026cd494bb3cd8 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Wed, 22 May 2019 18:29:04 -0700 Subject: spi: tegra114: set master cleanup and also invoke it on probe error This patch sets master cleanup and also invokes tegra spi clean on tegra spi probe failure to release tegra spi client data. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/spi/spi-tegra114.c') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 253a7f182fc9..15f9368fc0f8 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -966,6 +966,8 @@ static int tegra_spi_setup(struct spi_device *spi) ret = pm_runtime_get_sync(tspi->dev); if (ret < 0) { dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); + if (cdata) + tegra_spi_cleanup(spi); return ret; } @@ -1331,6 +1333,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->set_cs_timing = tegra_spi_set_hw_cs_timing; master->num_chipselect = MAX_CHIP_SELECT; -- cgit v1.2.3