diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index e46bc98327a9..8dc42acd9ef6 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -24,6 +24,7 @@ #include #include #include +#include #define SPI_NUM_CHIPSELECT (4) #define SPI_XFER_TIMEOUT_MS (250) @@ -67,6 +68,11 @@ /* SPI_TX/SPI_RX_TRANS_LEN fields */ #define TRANS_LEN_MSK (GENMASK(23, 0)) +/* SE_SPI_DELAY_COUNTERS */ +#define SPI_INTER_WORDS_DELAY_MSK (GENMASK(9, 0)) +#define SPI_CS_CLK_DELAY_MSK (GENMASK(19, 10)) +#define SPI_CS_CLK_DELAY_SHFT (10) + /* M_CMD OP codes for SPI */ #define SPI_TX_ONLY (1) #define SPI_RX_ONLY (2) @@ -229,6 +235,8 @@ static int setup_fifo_params(struct spi_device *spi_slv, int ret = 0; int idx; int div; + struct spi_geni_qcom_ctrl_data *delay_params = NULL; + u32 spi_delay_params = 0; loopback_cfg &= ~LOOPBACK_MSK; cpol &= ~CPOL; @@ -246,6 +254,22 @@ static int setup_fifo_params(struct spi_device *spi_slv, if (spi_slv->mode & SPI_CS_HIGH) demux_output_inv |= BIT(spi_slv->chip_select); + if (spi_slv->controller_data) { + u32 cs_clk_delay = 0; + u32 inter_words_delay = 0; + + delay_params = + (struct spi_geni_qcom_ctrl_data *) spi_slv->controller_data; + cs_clk_delay = + (delay_params->spi_cs_clk_delay << SPI_CS_CLK_DELAY_SHFT) + & SPI_CS_CLK_DELAY_MSK; + inter_words_delay = + delay_params->spi_inter_words_delay & + SPI_INTER_WORDS_DELAY_MSK; + spi_delay_params = + (inter_words_delay | cs_clk_delay); + } + demux_sel = spi_slv->chip_select; mas->cur_speed_hz = spi_slv->max_speed_hz; mas->cur_word_len = spi_slv->bits_per_word; @@ -267,12 +291,13 @@ static int setup_fifo_params(struct spi_device *spi_slv, geni_write_reg(demux_output_inv, mas->base, SE_SPI_DEMUX_OUTPUT_INV); geni_write_reg(clk_sel, mas->base, SE_GENI_CLK_SEL); geni_write_reg(m_clk_cfg, mas->base, GENI_SER_M_CLK_CFG); + geni_write_reg(spi_delay_params, mas->base, SE_SPI_DELAY_COUNTERS); GENI_SE_DBG(mas->ipc, false, mas->dev, "%s:Loopback%d demux_sel0x%x demux_op_inv 0x%x clk_cfg 0x%x\n", __func__, loopback_cfg, demux_sel, demux_output_inv, m_clk_cfg); GENI_SE_DBG(mas->ipc, false, mas->dev, - "%s:clk_sel 0x%x cpol %d cpha %d\n", __func__, - clk_sel, cpol, cpha); + "%s:clk_sel 0x%x cpol %d cpha %d delay 0x%x\n", __func__, + clk_sel, cpol, cpha, spi_delay_params); /* Ensure message level attributes are written before returning */ mb(); setup_fifo_params_exit: @@ -306,7 +331,8 @@ static int select_xfer_mode(struct spi_master *spi, } static struct msm_gpi_tre *setup_config0_tre(struct spi_transfer *xfer, - struct spi_geni_master *mas, u16 mode) + struct spi_geni_master *mas, u16 mode, + u32 cs_clk_delay, u32 inter_words_delay) { struct msm_gpi_tre *c0_tre = &mas->gsi[mas->num_xfers].config0_tre; u8 flags = 0; @@ -340,12 +366,16 @@ static struct msm_gpi_tre *setup_config0_tre(struct spi_transfer *xfer, } c0_tre->dword[0] = MSM_GPI_SPI_CONFIG0_TRE_DWORD0(pack, flags, word_len); - c0_tre->dword[1] = MSM_GPI_SPI_CONFIG0_TRE_DWORD1(0, 0, 0); + c0_tre->dword[1] = MSM_GPI_SPI_CONFIG0_TRE_DWORD1(0, cs_clk_delay, + inter_words_delay); c0_tre->dword[2] = MSM_GPI_SPI_CONFIG0_TRE_DWORD2(idx, div); c0_tre->dword[3] = MSM_GPI_SPI_CONFIG0_TRE_DWORD3(0, 0, 0, 1); GENI_SE_DBG(mas->ipc, false, mas->dev, "%s: flags 0x%x word %d pack %d idx %d div %d\n", __func__, flags, word_len, pack, idx, div); + GENI_SE_DBG(mas->ipc, false, mas->dev, + "%s: cs_clk_delay %d inter_words_delay %d\n", __func__, + cs_clk_delay, inter_words_delay); return c0_tre; } @@ -503,13 +533,27 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, u32 rx_len = 0; int go_flags = 0; unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + struct spi_geni_qcom_ctrl_data *delay_params = NULL; + u32 cs_clk_delay = 0; + u32 inter_words_delay = 0; + + if (spi_slv->controller_data) { + delay_params = + (struct spi_geni_qcom_ctrl_data *) spi_slv->controller_data; + + cs_clk_delay = + delay_params->spi_cs_clk_delay; + inter_words_delay = + delay_params->spi_inter_words_delay; + } if ((xfer->bits_per_word != mas->cur_word_len) || (xfer->speed_hz != mas->cur_speed_hz)) { mas->cur_word_len = xfer->bits_per_word; mas->cur_speed_hz = xfer->speed_hz; tx_nent++; - c0_tre = setup_config0_tre(xfer, mas, spi_slv->mode); + c0_tre = setup_config0_tre(xfer, mas, spi_slv->mode, + cs_clk_delay, inter_words_delay); if (IS_ERR_OR_NULL(c0_tre)) { dev_err(mas->dev, "%s:Err setting c0tre:%d\n", __func__, ret); diff --git a/include/linux/spi/spi-geni-qcom.h b/include/linux/spi/spi-geni-qcom.h new file mode 100644 index 000000000000..8aee88a04dee --- /dev/null +++ b/include/linux/spi/spi-geni-qcom.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SPI_GENI_QCOM_HEADER___ +#define __SPI_GENI_QCOM_HEADER___ + +struct spi_geni_qcom_ctrl_data { + u32 spi_cs_clk_delay; + u32 spi_inter_words_delay; +}; + +#endif /*__SPI_GENI_QCOM_HEADER___*/