From b06fe0e7e8a11d644dd61b3b0c9977a3edffe716 Mon Sep 17 00:00:00 2001 From: Girish Mahadevan Date: Wed, 6 Sep 2017 11:35:19 -0600 Subject: [PATCH] spi: spi-geni-qcom: Allow clients to specify SPI slave parameters Allow clients to specify SPI slave specific parameters such as timing delays that the SPI slave needs to close timing on SPI transactions. Change-Id: I0496e97d66746540593f10efc48ef5e60d8e7089 Signed-off-by: Girish Mahadevan --- drivers/spi/spi-geni-qcom.c | 54 ++++++++++++++++++++++++++++--- include/linux/spi/spi-geni-qcom.h | 23 +++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 include/linux/spi/spi-geni-qcom.h diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index de3dc6970c70..280e8bd42d01 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___*/