diff mbox series

[V4,3/4] spi: spi-geni-qcom: Add SPI driver support for GENI based QUP

Message ID 1537294047-12093-4-git-send-email-dkota@codeaurora.org (mailing list archive)
State New, archived
Headers show
Series spi-geni-qcom: QUP SPI GENI driver and SPI device tree bindings | expand

Commit Message

Dilip Kota Sept. 18, 2018, 6:07 p.m. UTC
From: Girish Mahadevan <girishm@codeaurora.org>

This driver supports GENI based SPI Controller in the Qualcomm SOCs. The
Qualcomm Generic Interface (GENI) is a programmable module supporting a
wide range of serial interfaces including SPI. This driver supports SPI
operations using FIFO mode of transfer.

Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
Signed-off-by: Dilip Kota <dkota@codeaurora.org>
---
Addressing all the reviewer commets given in Patchset1.
Summerizing the chages below:

	Move prepare_transfer_hardware to probe
	Fix the unbind failure
	Add Spinlock
	Use readl/writel() instead of _relaxed()
	Remove __func__ in dev_err
	Declare register variables as u32
	Don't initialize variables and then overwrite them
	Remove unused step
	Remove IRQ entry in geni struct
	Remove extra paranthesis
	Reframe probe errors
	Change the tx_fifo_width naming in handle_rx and handle_tx
	Declare spi_geni_master as 'mas' in complete driver.
	Correcting the punctuation in the comments.
	Adding comments in the code while setting Tx WaterMark Register.
	get_spi_clk_cfg() fixes
	Rewrite the geni_spi_handle_tx() and geni_spi_handle_rx()

Regarding the suspend/resume and loopback failures, i am working to update in next patch series
drivers/spi/Kconfig               |  12 +
 drivers/spi/Makefile              |   1 +
 drivers/spi/spi-geni-qcom.c       | 653 ++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi-geni-qcom.h |  14 +
 4 files changed, 680 insertions(+)
 create mode 100644 drivers/spi/spi-geni-qcom.c
 create mode 100644 include/linux/spi/spi-geni-qcom.h

Comments

Doug Anderson Sept. 18, 2018, 8:12 p.m. UTC | #1
Hi,

On Tue, Sep 18, 2018 at 11:09 AM Dilip Kota <dkota@codeaurora.org> wrote:
>
> From: Girish Mahadevan <girishm@codeaurora.org>
>
> This driver supports GENI based SPI Controller in the Qualcomm SOCs. The
> Qualcomm Generic Interface (GENI) is a programmable module supporting a
> wide range of serial interfaces including SPI. This driver supports SPI
> operations using FIFO mode of transfer.
>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> Signed-off-by: Dilip Kota <dkota@codeaurora.org>
> ---
> Addressing all the reviewer commets given in Patchset1.
> Summerizing the chages below:
>
>         Move prepare_transfer_hardware to probe
>         Fix the unbind failure
>         Add Spinlock
>         Use readl/writel() instead of _relaxed()
>         Remove __func__ in dev_err
>         Declare register variables as u32
>         Don't initialize variables and then overwrite them
>         Remove unused step
>         Remove IRQ entry in geni struct
>         Remove extra paranthesis
>         Reframe probe errors
>         Change the tx_fifo_width naming in handle_rx and handle_tx
>         Declare spi_geni_master as 'mas' in complete driver.
>         Correcting the punctuation in the comments.
>         Adding comments in the code while setting Tx WaterMark Register.
>         get_spi_clk_cfg() fixes
>         Rewrite the geni_spi_handle_tx() and geni_spi_handle_rx()
>
> Regarding the suspend/resume and loopback failures, i am working to update in next patch series
> drivers/spi/Kconfig               |  12 +
>  drivers/spi/Makefile              |   1 +
>  drivers/spi/spi-geni-qcom.c       | 653 ++++++++++++++++++++++++++++++++++++++
>  include/linux/spi/spi-geni-qcom.h |  14 +
>  4 files changed, 680 insertions(+)

I've confirmed that all the feedback I'm aware of from previous
versions of this patch has now been addressed, except as noted above:

* suspend callback still looks a little wonky.
* loopback doesn't actually work

IMO it would be OK to land this patch and address the suspend callback
/ loopback issues in later patches, but obviously that's for Mark to
decide.

NOTE: for this patch to land it must be based on a tree that includes
the patch ("soc: qcom: geni: Make version macros simpler") [1] in
order to compile.  It would also be nice if it was also based upon the
patches ("soc: qcom: geni: Don't ignore clk_round_rate() errors in
geni_se_clk_tbl_get()") [2] and ("soc: qcom: geni:
geni_se_clk_freq_match() should always accept multiples") [3].  Those
aren't needed for things to compile but are technically needed for
correctness.  +Andy Gross has said he'll put that the above 3 patches
in his tree in a way that it would be easy to pull in

Mark: it would be super if you had a chance to take a look at this
patch now and see what you think.

[1] https://lkml.kernel.org/r/20180518224750.232742-1-swboyd@chromium.org
[2] https://lkml.kernel.org/r/20180906224906.93752-1-dianders@chromium.org
[3] https://lkml.kernel.org/r/20180906224906.93752-2-dianders@chromium.org

With all that said, I'm happy adding:

Reviewed-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>


-Doug
kernel test robot Sept. 19, 2018, 12:41 a.m. UTC | #2
Hi Girish,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on spi/for-next]
[also build test ERROR on v4.19-rc4 next-20180918]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Dilip-Kota/spi-geni-qcom-QUP-SPI-GENI-driver-and-SPI-device-tree-bindings/20180919-043055
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next
config: xtensa-allyesconfig (attached as .config)
compiler: xtensa-linux-gcc (GCC) 8.1.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=8.1.0 make.cross ARCH=xtensa 

All errors (new ones prefixed by >>):

   drivers/spi/spi-geni-qcom.c: In function 'spi_geni_init':
>> drivers/spi/spi-geni-qcom.c:236:10: error: implicit declaration of function 'GENI_SE_VERSION_MAJOR'; did you mean 'XCHAL_HW_VERSION_MAJOR'? [-Werror=implicit-function-declaration]
     major = GENI_SE_VERSION_MAJOR(ver);
             ^~~~~~~~~~~~~~~~~~~~~
             XCHAL_HW_VERSION_MAJOR
>> drivers/spi/spi-geni-qcom.c:237:10: error: implicit declaration of function 'GENI_SE_VERSION_MINOR'; did you mean 'XCHAL_HW_VERSION_MINOR'? [-Werror=implicit-function-declaration]
     minor = GENI_SE_VERSION_MINOR(ver);
             ^~~~~~~~~~~~~~~~~~~~~
             XCHAL_HW_VERSION_MINOR
   cc1: some warnings being treated as errors

vim +236 drivers/spi/spi-geni-qcom.c

   208	
   209	static int spi_geni_init(struct spi_geni_master *mas)
   210	{
   211		struct geni_se *se = &mas->se;
   212		unsigned int proto, major, minor, ver;
   213	
   214		pm_runtime_get_sync(mas->dev);
   215	
   216		proto = geni_se_read_proto(se);
   217		if (proto != GENI_SE_SPI) {
   218			dev_err(mas->dev, "Invalid proto %d\n", proto);
   219			pm_runtime_put(mas->dev);
   220			return -ENXIO;
   221		}
   222		mas->tx_fifo_depth = geni_se_get_tx_fifo_depth(se);
   223		mas->rx_fifo_depth = geni_se_get_rx_fifo_depth(se);
   224	
   225		/* Width of Tx and Rx FIFO is same */
   226		mas->fifo_width_bits = geni_se_get_tx_fifo_width(se);
   227	
   228		/*
   229		 * Hardware programming guide suggests to configure
   230		 * RX FIFO RFR level to fifo_depth-2.
   231		 */
   232		geni_se_init(se, 0x0, mas->tx_fifo_depth - 2);
   233		/* Transmit an entire FIFO worth of data per IRQ */
   234		mas->tx_wm = 1;
   235		ver = geni_se_get_qup_hw_version(se);
 > 236		major = GENI_SE_VERSION_MAJOR(ver);
 > 237		minor = GENI_SE_VERSION_MINOR(ver);
   238	
   239		if (major == 1 && minor == 0)
   240			mas->oversampling = 2;
   241		else
   242			mas->oversampling = 1;
   243	
   244		pm_runtime_put(mas->dev);
   245		return 0;
   246	}
   247	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Stephen Boyd Sept. 24, 2018, 4:47 p.m. UTC | #3
Quoting Dilip Kota (2018-09-18 11:07:25)
> diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
> new file mode 100644
> index 0000000..949b853
> --- /dev/null
> +++ b/drivers/spi/spi-geni-qcom.c
> @@ -0,0 +1,653 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/log2.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/qcom-geni-se.h>
> +#include <linux/spi/spi.h>
> +#include <linux/spinlock.h>
> +
> +/* SPI SE specific registers and respective register fields */
> +#define SE_SPI_CPHA            0x224
> +#define CPHA                   BIT(0)
> +
> +#define SE_SPI_LOOPBACK                0x22c
> +#define LOOPBACK_ENABLE                0x1
> +#define NORMAL_MODE            0x0
> +#define LOOPBACK_MSK           GENMASK(1, 0)
> +
> +#define SE_SPI_CPOL            0x230
> +#define CPOL                   BIT(2)
> +
> +#define SE_SPI_DEMUX_OUTPUT_INV        0x24c
> +#define CS_DEMUX_OUTPUT_INV_MSK        GENMASK(3, 0)
> +
> +#define SE_SPI_DEMUX_SEL       0x250
> +#define CS_DEMUX_OUTPUT_SEL    GENMASK(3, 0)
> +
> +#define SE_SPI_TRANS_CFG       0x25c
> +#define CS_TOGGLE              BIT(0)
> +
> +#define SE_SPI_WORD_LEN                0x268
> +#define WORD_LEN_MSK           GENMASK(9, 0)
> +#define MIN_WORD_LEN           4
> +
> +#define SE_SPI_TX_TRANS_LEN    0x26c
> +#define SE_SPI_RX_TRANS_LEN    0x270
> +#define TRANS_LEN_MSK          GENMASK(23, 0)
> +
> +#define SE_SPI_PRE_POST_CMD_DLY        0x274
> +
> +#define SE_SPI_DELAY_COUNTERS  0x278
> +#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
> +#define SPI_FULL_DUPLEX                3
> +#define SPI_TX_RX              7
> +#define SPI_CS_ASSERT          8
> +#define SPI_CS_DEASSERT                9
> +#define SPI_SCK_ONLY           10
> +/* M_CMD params for SPI */
> +#define SPI_PRE_CMD_DELAY      BIT(0)
> +#define TIMESTAMP_BEFORE       BIT(1)
> +#define FRAGMENTATION          BIT(2)
> +#define TIMESTAMP_AFTER                BIT(3)
> +#define POST_CMD_DELAY         BIT(4)
> +
> +static irqreturn_t geni_spi_isr(int irq, void *data);

Does this need to be forward declared?

> +
> +struct spi_geni_master {
> +       struct geni_se se;
> +       struct device *dev;
> +       u32 rx_fifo_depth;

Is this used?

> +       u32 tx_fifo_depth;
> +       u32 fifo_width_bits;
> +       u32 tx_wm;
> +       unsigned int cur_speed_hz;

unsigned long for Hz? The clk framework uses that type.

> +       unsigned int cur_bits_per_word;
> +       unsigned int tx_rem_bytes;
> +       unsigned int rx_rem_bytes;
> +       struct spi_transfer *cur_xfer;

const?

> +       struct completion xfer_done;
> +       unsigned int oversampling;
> +       spinlock_t lock;
> +};
> +
[...]
> +
> +static int spi_geni_prepare_message(struct spi_master *spi,
> +                                       struct spi_message *spi_msg)
> +{
> +       int ret;
> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
> +       struct geni_se *se = &mas->se;
> +
> +       geni_se_select_mode(se, GENI_SE_FIFO);
> +       reinit_completion(&mas->xfer_done);
> +       ret = setup_fifo_params(spi_msg->spi, spi);
> +       if (ret)
> +               dev_err(mas->dev, "Couldn't select mode %d", ret);

Missing newline on error print.

> +       return ret;
> +}
> +
[...]
> +
> +static void setup_fifo_xfer(struct spi_transfer *xfer,
> +                               struct spi_geni_master *mas,
> +                               u16 mode, struct spi_master *spi)
> +{
> +       u32 m_cmd = 0, m_param = 0;
> +       u32 spi_tx_cfg, trans_len;
> +       struct geni_se *se = &mas->se;
> +
> +       spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG);
> +       if (xfer->bits_per_word != mas->cur_bits_per_word) {
> +               spi_setup_word_len(mas, mode, xfer->bits_per_word);
> +               mas->cur_bits_per_word = xfer->bits_per_word;
> +       }
> +
> +       /* Speed and bits per word can be overridden per transfer */
> +       if (xfer->speed_hz != mas->cur_speed_hz) {
> +               int ret;
> +               u32 clk_sel, m_clk_cfg;
> +               unsigned int idx, div;
> +
> +               ret = get_spi_clk_cfg(xfer->speed_hz, mas, &idx, &div);
> +               if (ret) {
> +                       dev_err(mas->dev, "Err setting clks:%d\n", ret);
> +                       return;
> +               }
> +               /*
> +                * SPI core clock gets configured with the requested frequency
> +                * or the frequency closer to the requested frequency.
> +                * For that reason requested frequency is stored in the
> +                * cur_speed_hz and referred in the consicutive transfer instead

s/consicutive/consecutive/

> +                * of calling clk_get_rate() API.
> +                */
> +               mas->cur_speed_hz = xfer->speed_hz;
> +               clk_sel = idx & CLK_SEL_MSK;
> +               m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
> +               writel(clk_sel, se->base + SE_GENI_CLK_SEL);
> +               writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG);
> +       }
> +
> +       mas->tx_rem_bytes = 0;
> +       mas->rx_rem_bytes = 0;
> +       if (xfer->tx_buf && xfer->rx_buf)
> +               m_cmd = SPI_FULL_DUPLEX;
> +       else if (xfer->tx_buf)
> +               m_cmd = SPI_TX_ONLY;
> +       else if (xfer->rx_buf)
> +               m_cmd = SPI_RX_ONLY;
> +
> +       spi_tx_cfg &= ~CS_TOGGLE;
> +       if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) {
> +               trans_len =
> +                       (xfer->len * BITS_PER_BYTE /
> +                                       mas->cur_bits_per_word) & TRANS_LEN_MSK;
> +       } else {
> +               unsigned int bytes_per_word =
> +                       mas->cur_bits_per_word / BITS_PER_BYTE + 1;
> +
> +               trans_len = (xfer->len / bytes_per_word) & TRANS_LEN_MSK;
> +       }

Rename 'trans_len' to 'len' and shorten some lines by taking out the
mask to get:

       if (!(mas->cur_bits_per_word % MIN_WORD_LEN))
               len = xfer->len * BITS_PER_BYTE / mas->cur_bits_per_word
       else 
               len = xfer->len / (mas->cur_bits_per_word / BITS_PER_BYTE + 1);
       len &= TRANS_LEN_MSK;

> +
> +       /*
> +        * If CS change flag is set, then toggle the CS line in between
> +        * transfers and keep CS asserted after the last transfer.
> +        * Else if keep CS flag asserted in between transfers and de-assert
> +        * CS after the last message.
> +        */
> +       if (xfer->cs_change) {
> +               if (list_is_last(&xfer->transfer_list,
> +                               &spi->cur_msg->transfers))
> +                       m_param = FRAGMENTATION;
> +       } else {
> +               if (!list_is_last(&xfer->transfer_list,
> +                               &spi->cur_msg->transfers))
> +                       m_param = FRAGMENTATION;
> +       }
> +
> +       mas->cur_xfer = xfer;
> +       if (m_cmd & SPI_TX_ONLY) {
> +               mas->tx_rem_bytes = xfer->len;
> +               writel(trans_len, se->base + SE_SPI_TX_TRANS_LEN);
> +       }
> +
> +       if (m_cmd & SPI_RX_ONLY) {
> +               writel(trans_len, se->base + SE_SPI_RX_TRANS_LEN);
> +               mas->rx_rem_bytes = xfer->len;
> +       }
> +       writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG);
> +       geni_se_setup_m_cmd(se, m_cmd, m_param);
> +
> +       /*
> +        * TX_WATERMARK_REG should be set after SPI configuration and
> +        * setting up GENI SE engine, as driver starts data transfer
> +        * for the watermark interrupt.
> +        */
> +       if (m_cmd & SPI_TX_ONLY)
> +               writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG);
> +}
> +
> +static void handle_fifo_timeout(struct spi_master *spi,
> +                               struct spi_message *msg)
> +{
> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
> +       unsigned long timeout, flags;
> +       struct geni_se *se = &mas->se;
> +
> +       spin_lock_irqsave(&mas->lock, flags);
> +       reinit_completion(&mas->xfer_done);
> +       geni_se_cancel_m_cmd(se);
> +       writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
> +       spin_unlock_irqrestore(&mas->lock, flags);
> +       timeout = wait_for_completion_timeout(&mas->xfer_done, HZ);

Can you rename this time_left or completed? Then the if condition reads
properly as "if not time left" or "if not completed". And then invert
that logic so things aren't so indented?

	time_left = wait_for_completion_timeout(..)
	if (time_left)
		return;

	spin_lock_irqsave(&mas->lock, flags);
	reinit_completion(..)
	...

> +       if (!timeout) {
> +               spin_lock_irqsave(&mas->lock, flags);
> +               reinit_completion(&mas->xfer_done);
> +               geni_se_abort_m_cmd(se);
> +               spin_unlock_irqrestore(&mas->lock, flags);
> +               timeout = wait_for_completion_timeout(&mas->xfer_done,
> +                                                               HZ);
> +               if (!timeout)
> +                       dev_err(mas->dev,
> +                               "Failed to cancel/abort m_cmd\n");
> +       }
> +}
> +
> +static int spi_geni_transfer_one(struct spi_master *spi,
> +                               struct spi_device *slv,
> +                               struct spi_transfer *xfer)
> +{
> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
> +
> +       setup_fifo_xfer(xfer, mas, slv->mode, spi);
> +       return 1;
> +}
> +
> +static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas)
> +{
> +       /*
> +        * Calculate how many bytes we'll put in each FIFO word.  If the
> +        * transfer words don't pack cleanly into a FIFO word we'll just put
> +        * one transfer word in each FIFO word.  If they do pack we'll pack 'em.
> +        */
> +       if (mas->fifo_width_bits % mas->cur_bits_per_word)
> +               return roundup_pow_of_two(DIV_ROUND_UP(mas->cur_bits_per_word,
> +                                                      BITS_PER_BYTE));
> +       else
> +               return mas->fifo_width_bits / BITS_PER_BYTE;

Just do a return. No else please.

> +}
> +
> +static irqreturn_t geni_spi_handle_tx(struct spi_geni_master *mas)
> +{
> +       struct geni_se *se = &mas->se;
> +       unsigned int max_bytes;
> +       const u8 *tx_buf;
> +       unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas);
> +       unsigned int i = 0;
> +
> +       if (!mas->cur_xfer)
> +               return IRQ_NONE;
> +
> +       max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * bytes_per_fifo_word;
> +       if (mas->tx_rem_bytes < max_bytes)
> +               max_bytes = mas->tx_rem_bytes;
> +
> +       tx_buf = mas->cur_xfer->tx_buf + mas->cur_xfer->len - mas->tx_rem_bytes;
> +       while (i < max_bytes) {
> +               unsigned int j;
> +               unsigned int bytes_to_write;
> +               u32 fifo_word = 0;
> +               u8 *fifo_byte = (u8 *)&fifo_word;
> +
> +               bytes_to_write = min(bytes_per_fifo_word, max_bytes - i);
> +               for (j = 0; j < bytes_to_write; j++)
> +                       fifo_byte[j] = tx_buf[i++];
> +               iowrite32_rep(se->base + SE_GENI_TX_FIFOn, &fifo_word, 1);
> +       }
> +       mas->tx_rem_bytes -= max_bytes;
> +       if (!mas->tx_rem_bytes)
> +               writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t geni_spi_handle_rx(struct spi_geni_master *mas)
> +{
> +       struct geni_se *se = &mas->se;
> +       u32 rx_fifo_status;
> +       unsigned int rx_bytes;
> +       u8 *rx_buf;
> +       unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas);
> +       unsigned int i = 0;
> +
> +       if (!mas->cur_xfer)
> +               return IRQ_NONE;
> +
> +       rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS);

Rename to 'status'? rx_fifo is implicit in the register macro.

> +       rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * bytes_per_fifo_word;
> +       if (rx_fifo_status & RX_LAST) {
> +               unsigned int rx_last_byte_valid =
> +                       (rx_fifo_status & RX_LAST_BYTE_VALID_MSK)
> +                               >> RX_LAST_BYTE_VALID_SHFT;

Hoist this variable into function scope? So then the line is:

		last_valid = status & RX_LAST_BYTE_VALID_MSK;
		last_valid >>= RX_LAST_BYTE_VALID_SHFT;

> +               if (rx_last_byte_valid && (rx_last_byte_valid < 4))

Drop useless parenthesis please.

> +                       rx_bytes -= bytes_per_fifo_word - rx_last_byte_valid;
> +       }
> +       if (mas->rx_rem_bytes < rx_bytes)
> +               rx_bytes = mas->rx_rem_bytes;
> +
> +       rx_buf = mas->cur_xfer->rx_buf + mas->cur_xfer->len - mas->rx_rem_bytes;
> +       while (i < rx_bytes) {
> +               u32 fifo_word = 0;
> +               u8 *fifo_byte = (u8 *)&fifo_word;
> +               unsigned int bytes_to_read;
> +               unsigned int j;
> +
> +               bytes_to_read = min(bytes_per_fifo_word, rx_bytes - i);
> +               ioread32_rep(se->base + SE_GENI_RX_FIFOn, &fifo_word, 1);
> +               for (j = 0; j < bytes_to_read; j++)
> +                       rx_buf[i++] = fifo_byte[j];
> +       }
> +       mas->rx_rem_bytes -= rx_bytes;
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t geni_spi_isr(int irq, void *data)
> +{
> +       struct spi_master *spi = data;
> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
> +       struct geni_se *se = &mas->se;
> +       u32 m_irq;
> +       unsigned long flags;
> +       irqreturn_t ret = IRQ_HANDLED;
> +
> +       if (pm_runtime_status_suspended(mas->dev))
> +               return IRQ_NONE;
> +
> +       spin_lock_irqsave(&mas->lock, flags);
> +       m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
> +       if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN))
> +               ret = geni_spi_handle_rx(mas);
> +
> +       if (m_irq & M_TX_FIFO_WATERMARK_EN)
> +               ret = geni_spi_handle_tx(mas);

Is it possible for all three conditions above to happen in one
interrupt? I ask because 'ret' is overwritten and so what may have been
IRQ_HANDLED may become IRQ_NONE which will lead to confusion in the irq
layer. Maybe the handle tx/rx functions can return a bool, that gets
orred together each time so that we know if something handled an
interrupt?

> +
> +       if (m_irq & M_CMD_DONE_EN) {
> +               spi_finalize_current_transfer(spi);
> +               /*
> +                * If this happens, then a CMD_DONE came before all the Tx
> +                * buffer bytes were sent out. This is unusual, log this
> +                * condition and disable the WM interrupt to prevent the
> +                * system from stalling due an interrupt storm.
> +                * If this happens when all Rx bytes haven't been received, log
> +                * the condition.
> +                * The only known time this can happen is if bits_per_word != 8
> +                * and some registers that expect xfer lengths in num spi_words
> +                * weren't written correctly.
> +                */
> +               if (mas->tx_rem_bytes) {
> +                       writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
> +                       dev_err(mas->dev, "Premature Done.tx_rem%d bpw%d\n",

Why isn't there a space after "Done."? And why is 'done' capitalized?
Should 'tx_rem' be 'tx_rem='?

> +                               mas->tx_rem_bytes, mas->cur_bits_per_word);
> +               }
> +               if (mas->rx_rem_bytes)
> +                       dev_err(mas->dev, "Premature Done.rx_rem%d bpw%d\n",
> +                               mas->rx_rem_bytes, mas->cur_bits_per_word);
> +       }
> +
> +       if ((m_irq & M_CMD_CANCEL_EN) || (m_irq & M_CMD_ABORT_EN))
> +               complete(&mas->xfer_done);
> +
> +       writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR);
> +       spin_unlock_irqrestore(&mas->lock, flags);
> +       return ret;
> +}
> +
> +static int spi_geni_probe(struct platform_device *pdev)
> +{
> +       int ret;
> +       struct spi_master *spi;
> +       struct spi_geni_master *mas;
> +       struct resource *res;
> +       struct geni_se *se;
> +
> +       spi = spi_alloc_master(&pdev->dev, sizeof(struct spi_geni_master));

sizeof(*mas) for code clarity?

> +       if (!spi)
> +               return -ENOMEM;
> +
> +       platform_set_drvdata(pdev, spi);
> +       mas = spi_master_get_devdata(spi);
> +       mas->dev = &pdev->dev;
> +       mas->se.dev = &pdev->dev;
> +       mas->se.wrapper = dev_get_drvdata(pdev->dev.parent);
> +       se = &mas->se;
> +
> +       spi->bus_num = -1;
> +       spi->dev.of_node = pdev->dev.of_node;
> +       mas->se.clk = devm_clk_get(&pdev->dev, "se");
> +       if (IS_ERR(mas->se.clk)) {
> +               ret = PTR_ERR(mas->se.clk);
> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> +               goto spi_geni_probe_err;
> +       }
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       se->base = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(se->base)) {
> +               ret = -ENOMEM;

		ret = PTR_ERR(se->base);

so we don't lose the error value.

> +               goto spi_geni_probe_err;
> +       }
> +
> +       ret = platform_get_irq(pdev, 0);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Err getting IRQ %d\n", ret);
> +               goto spi_geni_probe_err;
> +       }
> +       ret = devm_request_irq(&pdev->dev, ret, geni_spi_isr,
> +                               IRQF_TRIGGER_HIGH, "spi_geni", spi);
> +       if (ret)
> +               goto spi_geni_probe_err;

Can you request this irq as late as possible in the probe function? I
worry there may be some pending irq line set and then this will cause an
interrupt storm with IRQ_NONE returned because the device is runtime
suspended.

> +
> +       spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
> +       spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
> +       spi->num_chipselect = 4;
> +       spi->max_speed_hz = 50000000;
> +       spi->prepare_message = spi_geni_prepare_message;
> +       spi->transfer_one = spi_geni_transfer_one;
> +       spi->auto_runtime_pm = true;
> +       spi->handle_err = handle_fifo_timeout;
> +
> +       init_completion(&mas->xfer_done);
> +       spin_lock_init(&mas->lock);
> +       pm_runtime_enable(&pdev->dev);
> +
> +       ret = spi_geni_init(mas);
> +       if (ret)
> +               goto spi_geni_probe_runtime_disable;
> +
> +       ret = spi_register_master(spi);
> +       if (ret)
> +               goto spi_geni_probe_runtime_disable;
> +
> +       return 0;
> +spi_geni_probe_runtime_disable:
> +       pm_runtime_disable(&pdev->dev);
> +spi_geni_probe_err:
> +       spi_master_put(spi);
> +       return ret;
> +}
> +
> +static int spi_geni_remove(struct platform_device *pdev)
> +{
> +       struct spi_master *spi = platform_get_drvdata(pdev);
> +
> +       /* Unregister _before_ disabling pm_runtime() so we stop transfers */
> +       spi_unregister_master(spi);
> +
> +       pm_runtime_disable(&pdev->dev);
> +       return 0;
> +}
> +
> +static int __maybe_unused spi_geni_runtime_suspend(struct device *dev)
> +{
> +       struct spi_master *spi = dev_get_drvdata(dev);
> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
> +
> +       return geni_se_resources_off(&mas->se);
> +}
> +
> +static int __maybe_unused spi_geni_runtime_resume(struct device *dev)
> +{
> +       struct spi_master *spi = dev_get_drvdata(dev);
> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
> +
> +       return geni_se_resources_on(&mas->se);
> +}
> +
> +static int __maybe_unused spi_geni_suspend(struct device *dev)
> +{
> +       if (!pm_runtime_status_suspended(dev))
> +               return -EBUSY;
> +       return 0;
> +}
> +
> +static const struct dev_pm_ops spi_geni_pm_ops = {
> +       SET_RUNTIME_PM_OPS(spi_geni_runtime_suspend,
> +                                       spi_geni_runtime_resume, NULL)
> +       SET_SYSTEM_SLEEP_PM_OPS(spi_geni_suspend, NULL)
> +};
> +
> +static const struct of_device_id spi_geni_dt_match[] = {
> +       { .compatible = "qcom,geni-spi" },
> +       {}
> +};

Please add a MODULE_DEVICE_TABLE(of, spi_geni_dt_match) here.

> +
> +static struct platform_driver spi_geni_driver = {
> +       .probe  = spi_geni_probe,
> +       .remove = spi_geni_remove,
> +       .driver = {
> +               .name = "geni_spi",
> +               .pm = &spi_geni_pm_ops,
> +               .of_match_table = spi_geni_dt_match,
> +       },
> +};
> +module_platform_driver(spi_geni_driver);
> +
> +MODULE_DESCRIPTION("SPI driver for GENI based QUP cores");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/spi/spi-geni-qcom.h b/include/linux/spi/spi-geni-qcom.h
> new file mode 100644
> index 0000000..dc95764
> --- /dev/null
> +++ b/include/linux/spi/spi-geni-qcom.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> + */
> +
> +#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;

It was decided we still needed this? I don't see it in v3 so please
remove this file unless it's needed.
Dilip Kota Sept. 25, 2018, 7:36 p.m. UTC | #4
[...]
>> +#define TIMESTAMP_BEFORE       BIT(1)
>> +#define FRAGMENTATION          BIT(2)
>> +#define TIMESTAMP_AFTER                BIT(3)
>> +#define POST_CMD_DELAY         BIT(4)
>> +
>> +static irqreturn_t geni_spi_isr(int irq, void *data);
> 
> Does this need to be forward declared?
Not required, will remove it.
> 
>> +
>> +struct spi_geni_master {
>> +       struct geni_se se;
>> +       struct device *dev;
>> +       u32 rx_fifo_depth;
> 
> Is this used?
It can be removed.
> 
>> +       u32 tx_fifo_depth;
>> +       u32 fifo_width_bits;
>> +       u32 tx_wm;
>> +       unsigned int cur_speed_hz;
> 
> unsigned long for Hz? The clk framework uses that type.

cur_speed_hz stores the speed value requested as part of transfer (not 
the resultant or rounded off frequency got from clk framework. It is u32 
type, i will change cur_speed_hz to u32 type instead of unsigned long.
Code snippet:
         mas->cur_speed_hz = xfer->speed_hz;

> 
>> +       unsigned int cur_bits_per_word;
>> +       unsigned int tx_rem_bytes;
>> +       unsigned int rx_rem_bytes;
>> +       struct spi_transfer *cur_xfer;
> 
> const?
Yes can be marked as const, will update it.
> 
>> +       struct completion xfer_done;
>> +       unsigned int oversampling;
>> +       spinlock_t lock;
>> +};
>> +
> [...]
>> +
>> +static int spi_geni_prepare_message(struct spi_master *spi,
>> +                                       struct spi_message *spi_msg)
>> +{
>> +       int ret;
>> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
>> +       struct geni_se *se = &mas->se;
>> +
>> +       geni_se_select_mode(se, GENI_SE_FIFO);
>> +       reinit_completion(&mas->xfer_done);
>> +       ret = setup_fifo_params(spi_msg->spi, spi);
>> +       if (ret)
>> +               dev_err(mas->dev, "Couldn't select mode %d", ret);
> 
> Missing newline on error print.
> 
Will add it.
>> +       return ret;
>> +}
>> +
> [...]
>> +
>> +static void setup_fifo_xfer(struct spi_transfer *xfer,
>> +                               struct spi_geni_master *mas,
>> +                               u16 mode, struct spi_master *spi)
>> +{
>> +       u32 m_cmd = 0, m_param = 0;
>> +       u32 spi_tx_cfg, trans_len;
>> +       struct geni_se *se = &mas->se;
>> +
>> +       spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG);
>> +       if (xfer->bits_per_word != mas->cur_bits_per_word) {
>> +               spi_setup_word_len(mas, mode, xfer->bits_per_word);
>> +               mas->cur_bits_per_word = xfer->bits_per_word;
>> +       }
>> +
>> +       /* Speed and bits per word can be overridden per transfer */
>> +       if (xfer->speed_hz != mas->cur_speed_hz) {
>> +               int ret;
>> +               u32 clk_sel, m_clk_cfg;
>> +               unsigned int idx, div;
>> +
>> +               ret = get_spi_clk_cfg(xfer->speed_hz, mas, &idx, 
>> &div);
>> +               if (ret) {
>> +                       dev_err(mas->dev, "Err setting clks:%d\n", 
>> ret);
>> +                       return;
>> +               }
>> +               /*
>> +                * SPI core clock gets configured with the requested 
>> frequency
>> +                * or the frequency closer to the requested frequency.
>> +                * For that reason requested frequency is stored in 
>> the
>> +                * cur_speed_hz and referred in the consicutive 
>> transfer instead
> 
> s/consicutive/consecutive/
will correct it.
> 
>> +                * of calling clk_get_rate() API.
>> +                */
>> +               mas->cur_speed_hz = xfer->speed_hz;
>> +               clk_sel = idx & CLK_SEL_MSK;
>> +               m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
>> +               writel(clk_sel, se->base + SE_GENI_CLK_SEL);
>> +               writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG);
>> +       }
>> +
>> +       mas->tx_rem_bytes = 0;
>> +       mas->rx_rem_bytes = 0;
>> +       if (xfer->tx_buf && xfer->rx_buf)
>> +               m_cmd = SPI_FULL_DUPLEX;
>> +       else if (xfer->tx_buf)
>> +               m_cmd = SPI_TX_ONLY;
>> +       else if (xfer->rx_buf)
>> +               m_cmd = SPI_RX_ONLY;
>> +
>> +       spi_tx_cfg &= ~CS_TOGGLE;
>> +       if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) {
>> +               trans_len =
>> +                       (xfer->len * BITS_PER_BYTE /
>> +                                       mas->cur_bits_per_word) & 
>> TRANS_LEN_MSK;
>> +       } else {
>> +               unsigned int bytes_per_word =
>> +                       mas->cur_bits_per_word / BITS_PER_BYTE + 1;
>> +
>> +               trans_len = (xfer->len / bytes_per_word) & 
>> TRANS_LEN_MSK;
>> +       }
> 
> Rename 'trans_len' to 'len' and shorten some lines by taking out the
> mask to get:
> 
>        if (!(mas->cur_bits_per_word % MIN_WORD_LEN))
>                len = xfer->len * BITS_PER_BYTE / mas->cur_bits_per_word
>        else
>                len = xfer->len / (mas->cur_bits_per_word / 
> BITS_PER_BYTE + 1);
>        len &= TRANS_LEN_MSK;
> 
OK
>> +
>> +       /*
>> +        * If CS change flag is set, then toggle the CS line in 
>> between
>> +        * transfers and keep CS asserted after the last transfer.
>> +        * Else if keep CS flag asserted in between transfers and 
>> de-assert
>> +        * CS after the last message.
>> +        */
>> +       if (xfer->cs_change) {
>> +               if (list_is_last(&xfer->transfer_list,
>> +                               &spi->cur_msg->transfers))
>> +                       m_param = FRAGMENTATION;
>> +       } else {
>> +               if (!list_is_last(&xfer->transfer_list,
>> +                               &spi->cur_msg->transfers))
>> +                       m_param = FRAGMENTATION;
>> +       }
>> +
>> +       mas->cur_xfer = xfer;
>> +       if (m_cmd & SPI_TX_ONLY) {
>> +               mas->tx_rem_bytes = xfer->len;
>> +               writel(trans_len, se->base + SE_SPI_TX_TRANS_LEN);
>> +       }
>> +
>> +       if (m_cmd & SPI_RX_ONLY) {
>> +               writel(trans_len, se->base + SE_SPI_RX_TRANS_LEN);
>> +               mas->rx_rem_bytes = xfer->len;
>> +       }
>> +       writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG);
>> +       geni_se_setup_m_cmd(se, m_cmd, m_param);
>> +
>> +       /*
>> +        * TX_WATERMARK_REG should be set after SPI configuration and
>> +        * setting up GENI SE engine, as driver starts data transfer
>> +        * for the watermark interrupt.
>> +        */
>> +       if (m_cmd & SPI_TX_ONLY)
>> +               writel(mas->tx_wm, se->base + 
>> SE_GENI_TX_WATERMARK_REG);
>> +}
>> +
>> +static void handle_fifo_timeout(struct spi_master *spi,
>> +                               struct spi_message *msg)
>> +{
>> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
>> +       unsigned long timeout, flags;
>> +       struct geni_se *se = &mas->se;
>> +
>> +       spin_lock_irqsave(&mas->lock, flags);
>> +       reinit_completion(&mas->xfer_done);
>> +       geni_se_cancel_m_cmd(se);
>> +       writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
>> +       spin_unlock_irqrestore(&mas->lock, flags);
>> +       timeout = wait_for_completion_timeout(&mas->xfer_done, HZ);
> 
> Can you rename this time_left or completed? Then the if condition reads
> properly as "if not time left" or "if not completed". And then invert
> that logic so things aren't so indented?
> 
> 	time_left = wait_for_completion_timeout(..)
> 	if (time_left)
> 		return;
> 
> 	spin_lock_irqsave(&mas->lock, flags);
> 	reinit_completion(..)
> 	...
> 
Ok
>> +       if (!timeout) {
>> +               spin_lock_irqsave(&mas->lock, flags);
>> +               reinit_completion(&mas->xfer_done);
>> +               geni_se_abort_m_cmd(se);
>> +               spin_unlock_irqrestore(&mas->lock, flags);
>> +               timeout = wait_for_completion_timeout(&mas->xfer_done,
>> +                                                               HZ);
>> +               if (!timeout)
>> +                       dev_err(mas->dev,
>> +                               "Failed to cancel/abort m_cmd\n");
>> +       }
>> +}
>> +
>> +static int spi_geni_transfer_one(struct spi_master *spi,
>> +                               struct spi_device *slv,
>> +                               struct spi_transfer *xfer)
>> +{
>> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
>> +
>> +       setup_fifo_xfer(xfer, mas, slv->mode, spi);
>> +       return 1;
>> +}
>> +
>> +static unsigned int geni_byte_per_fifo_word(struct spi_geni_master 
>> *mas)
>> +{
>> +       /*
>> +        * Calculate how many bytes we'll put in each FIFO word.  If 
>> the
>> +        * transfer words don't pack cleanly into a FIFO word we'll 
>> just put
>> +        * one transfer word in each FIFO word.  If they do pack we'll 
>> pack 'em.
>> +        */
>> +       if (mas->fifo_width_bits % mas->cur_bits_per_word)
>> +               return 
>> roundup_pow_of_two(DIV_ROUND_UP(mas->cur_bits_per_word,
>> +                                                      
>> BITS_PER_BYTE));
>> +       else
>> +               return mas->fifo_width_bits / BITS_PER_BYTE;
> 
> Just do a return. No else please.
> 
Ok
>> +}
>> +
>> +static irqreturn_t geni_spi_handle_tx(struct spi_geni_master *mas)
>> +{
>> +       struct geni_se *se = &mas->se;
>> +       unsigned int max_bytes;
>> +       const u8 *tx_buf;
>> +       unsigned int bytes_per_fifo_word = 
>> geni_byte_per_fifo_word(mas);
>> +       unsigned int i = 0;
>> +
>> +       if (!mas->cur_xfer)
>> +               return IRQ_NONE;
>> +
>> +       max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * 
>> bytes_per_fifo_word;
>> +       if (mas->tx_rem_bytes < max_bytes)
>> +               max_bytes = mas->tx_rem_bytes;
>> +
>> +       tx_buf = mas->cur_xfer->tx_buf + mas->cur_xfer->len - 
>> mas->tx_rem_bytes;
>> +       while (i < max_bytes) {
>> +               unsigned int j;
>> +               unsigned int bytes_to_write;
>> +               u32 fifo_word = 0;
>> +               u8 *fifo_byte = (u8 *)&fifo_word;
>> +
>> +               bytes_to_write = min(bytes_per_fifo_word, max_bytes - 
>> i);
>> +               for (j = 0; j < bytes_to_write; j++)
>> +                       fifo_byte[j] = tx_buf[i++];
>> +               iowrite32_rep(se->base + SE_GENI_TX_FIFOn, &fifo_word, 
>> 1);
>> +       }
>> +       mas->tx_rem_bytes -= max_bytes;
>> +       if (!mas->tx_rem_bytes)
>> +               writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t geni_spi_handle_rx(struct spi_geni_master *mas)
>> +{
>> +       struct geni_se *se = &mas->se;
>> +       u32 rx_fifo_status;
>> +       unsigned int rx_bytes;
>> +       u8 *rx_buf;
>> +       unsigned int bytes_per_fifo_word = 
>> geni_byte_per_fifo_word(mas);
>> +       unsigned int i = 0;
>> +
>> +       if (!mas->cur_xfer)
>> +               return IRQ_NONE;
>> +
>> +       rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS);
> 
> Rename to 'status'? rx_fifo is implicit in the register macro.
Declared as "rx_fifo_status" for code readability.

> 
>> +       rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * 
>> bytes_per_fifo_word;
>> +       if (rx_fifo_status & RX_LAST) {
>> +               unsigned int rx_last_byte_valid =
>> +                       (rx_fifo_status & RX_LAST_BYTE_VALID_MSK)
>> +                               >> RX_LAST_BYTE_VALID_SHFT;
> 
> Hoist this variable into function scope? So then the line is:
> 
> 		last_valid = status & RX_LAST_BYTE_VALID_MSK;
> 		last_valid >>= RX_LAST_BYTE_VALID_SHFT;
> 
Ok
>> +               if (rx_last_byte_valid && (rx_last_byte_valid < 4))
> 
> Drop useless parenthesis please.
> 
Ok
>> +                       rx_bytes -= bytes_per_fifo_word - 
>> rx_last_byte_valid;
>> +       }
>> +       if (mas->rx_rem_bytes < rx_bytes)
>> +               rx_bytes = mas->rx_rem_bytes;
>> +
>> +       rx_buf = mas->cur_xfer->rx_buf + mas->cur_xfer->len - 
>> mas->rx_rem_bytes;
>> +       while (i < rx_bytes) {
>> +               u32 fifo_word = 0;
>> +               u8 *fifo_byte = (u8 *)&fifo_word;
>> +               unsigned int bytes_to_read;
>> +               unsigned int j;
>> +
>> +               bytes_to_read = min(bytes_per_fifo_word, rx_bytes - 
>> i);
>> +               ioread32_rep(se->base + SE_GENI_RX_FIFOn, &fifo_word, 
>> 1);
>> +               for (j = 0; j < bytes_to_read; j++)
>> +                       rx_buf[i++] = fifo_byte[j];
>> +       }
>> +       mas->rx_rem_bytes -= rx_bytes;
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t geni_spi_isr(int irq, void *data)
>> +{
>> +       struct spi_master *spi = data;
>> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
>> +       struct geni_se *se = &mas->se;
>> +       u32 m_irq;
>> +       unsigned long flags;
>> +       irqreturn_t ret = IRQ_HANDLED;
>> +
>> +       if (pm_runtime_status_suspended(mas->dev))
>> +               return IRQ_NONE;
>> +
>> +       spin_lock_irqsave(&mas->lock, flags);
>> +       m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
>> +       if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & 
>> M_RX_FIFO_LAST_EN))
>> +               ret = geni_spi_handle_rx(mas);
>> +
>> +       if (m_irq & M_TX_FIFO_WATERMARK_EN)
>> +               ret = geni_spi_handle_tx(mas);
> 
> Is it possible for all three conditions above to happen in one
> interrupt? I ask because 'ret' is overwritten and so what may have been
> IRQ_HANDLED may become IRQ_NONE which will lead to confusion in the irq
> layer. Maybe the handle tx/rx functions can return a bool, that gets
> orred together each time so that we know if something handled an
> interrupt?
> 
Will check it again by running fullduplex transfer and update.
>> +
>> +       if (m_irq & M_CMD_DONE_EN) {
>> +               spi_finalize_current_transfer(spi);
>> +               /*
>> +                * If this happens, then a CMD_DONE came before all 
>> the Tx
>> +                * buffer bytes were sent out. This is unusual, log 
>> this
>> +                * condition and disable the WM interrupt to prevent 
>> the
>> +                * system from stalling due an interrupt storm.
>> +                * If this happens when all Rx bytes haven't been 
>> received, log
>> +                * the condition.
>> +                * The only known time this can happen is if 
>> bits_per_word != 8
>> +                * and some registers that expect xfer lengths in num 
>> spi_words
>> +                * weren't written correctly.
>> +                */
>> +               if (mas->tx_rem_bytes) {
>> +                       writel(0, se->base + 
>> SE_GENI_TX_WATERMARK_REG);
>> +                       dev_err(mas->dev, "Premature Done.tx_rem%d 
>> bpw%d\n",
> 
> Why isn't there a space after "Done."? And why is 'done' capitalized?
> Should 'tx_rem' be 'tx_rem='?
Yes, will update.
> 
>> +                               mas->tx_rem_bytes, 
>> mas->cur_bits_per_word);
>> +               }
>> +               if (mas->rx_rem_bytes)
>> +                       dev_err(mas->dev, "Premature Done.rx_rem%d 
>> bpw%d\n",
>> +                               mas->rx_rem_bytes, 
>> mas->cur_bits_per_word);
>> +       }
>> +
>> +       if ((m_irq & M_CMD_CANCEL_EN) || (m_irq & M_CMD_ABORT_EN))
>> +               complete(&mas->xfer_done);
>> +
>> +       writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR);
>> +       spin_unlock_irqrestore(&mas->lock, flags);
>> +       return ret;
>> +}
>> +
>> +static int spi_geni_probe(struct platform_device *pdev)
>> +{
>> +       int ret;
>> +       struct spi_master *spi;
>> +       struct spi_geni_master *mas;
>> +       struct resource *res;
>> +       struct geni_se *se;
>> +
>> +       spi = spi_alloc_master(&pdev->dev, sizeof(struct 
>> spi_geni_master));
> 
> sizeof(*mas) for code clarity?
> 
Ok
>> +       if (!spi)
>> +               return -ENOMEM;
>> +
>> +       platform_set_drvdata(pdev, spi);
>> +       mas = spi_master_get_devdata(spi);
>> +       mas->dev = &pdev->dev;
>> +       mas->se.dev = &pdev->dev;
>> +       mas->se.wrapper = dev_get_drvdata(pdev->dev.parent);
>> +       se = &mas->se;
>> +
>> +       spi->bus_num = -1;
>> +       spi->dev.of_node = pdev->dev.of_node;
>> +       mas->se.clk = devm_clk_get(&pdev->dev, "se");
>> +       if (IS_ERR(mas->se.clk)) {
>> +               ret = PTR_ERR(mas->se.clk);
>> +               dev_err(&pdev->dev, "Err getting SE Core clk %d\n", 
>> ret);
>> +               goto spi_geni_probe_err;
>> +       }
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       se->base = devm_ioremap_resource(&pdev->dev, res);
>> +       if (IS_ERR(se->base)) {
>> +               ret = -ENOMEM;
> 
> 		ret = PTR_ERR(se->base);
> 
> so we don't lose the error value.
> 
Ok
>> +               goto spi_geni_probe_err;
>> +       }
>> +
>> +       ret = platform_get_irq(pdev, 0);
>> +       if (ret < 0) {
>> +               dev_err(&pdev->dev, "Err getting IRQ %d\n", ret);
>> +               goto spi_geni_probe_err;
>> +       }
>> +       ret = devm_request_irq(&pdev->dev, ret, geni_spi_isr,
>> +                               IRQF_TRIGGER_HIGH, "spi_geni", spi);
>> +       if (ret)
>> +               goto spi_geni_probe_err;
> 
> Can you request this irq as late as possible in the probe function? I
> worry there may be some pending irq line set and then this will cause 
> an
> interrupt storm with IRQ_NONE returned because the device is runtime
> suspended.
> 
Ok; will call it after spi_register_master()
>> +
>> +       spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
>> +       spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
>> +       spi->num_chipselect = 4;
>> +       spi->max_speed_hz = 50000000;
>> +       spi->prepare_message = spi_geni_prepare_message;
>> +       spi->transfer_one = spi_geni_transfer_one;
>> +       spi->auto_runtime_pm = true;
>> +       spi->handle_err = handle_fifo_timeout;
>> +
>> +       init_completion(&mas->xfer_done);
>> +       spin_lock_init(&mas->lock);
>> +       pm_runtime_enable(&pdev->dev);
>> +
>> +       ret = spi_geni_init(mas);
>> +       if (ret)
>> +               goto spi_geni_probe_runtime_disable;
>> +
>> +       ret = spi_register_master(spi);
>> +       if (ret)
>> +               goto spi_geni_probe_runtime_disable;
>> +
>> +       return 0;
>> +spi_geni_probe_runtime_disable:
>> +       pm_runtime_disable(&pdev->dev);
>> +spi_geni_probe_err:
>> +       spi_master_put(spi);
>> +       return ret;
>> +}
>> +
>> +static int spi_geni_remove(struct platform_device *pdev)
>> +{
>> +       struct spi_master *spi = platform_get_drvdata(pdev);
>> +
>> +       /* Unregister _before_ disabling pm_runtime() so we stop 
>> transfers */
>> +       spi_unregister_master(spi);
>> +
>> +       pm_runtime_disable(&pdev->dev);
>> +       return 0;
>> +}
>> +
>> +static int __maybe_unused spi_geni_runtime_suspend(struct device 
>> *dev)
>> +{
>> +       struct spi_master *spi = dev_get_drvdata(dev);
>> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
>> +
>> +       return geni_se_resources_off(&mas->se);
>> +}
>> +
>> +static int __maybe_unused spi_geni_runtime_resume(struct device *dev)
>> +{
>> +       struct spi_master *spi = dev_get_drvdata(dev);
>> +       struct spi_geni_master *mas = spi_master_get_devdata(spi);
>> +
>> +       return geni_se_resources_on(&mas->se);
>> +}
>> +
>> +static int __maybe_unused spi_geni_suspend(struct device *dev)
>> +{
>> +       if (!pm_runtime_status_suspended(dev))
>> +               return -EBUSY;
>> +       return 0;
>> +}
>> +
>> +static const struct dev_pm_ops spi_geni_pm_ops = {
>> +       SET_RUNTIME_PM_OPS(spi_geni_runtime_suspend,
>> +                                       spi_geni_runtime_resume, NULL)
>> +       SET_SYSTEM_SLEEP_PM_OPS(spi_geni_suspend, NULL)
>> +};
>> +
>> +static const struct of_device_id spi_geni_dt_match[] = {
>> +       { .compatible = "qcom,geni-spi" },
>> +       {}
>> +};
> 
> Please add a MODULE_DEVICE_TABLE(of, spi_geni_dt_match) here.
> 
Ok
>> +
>> +static struct platform_driver spi_geni_driver = {
>> +       .probe  = spi_geni_probe,
>> +       .remove = spi_geni_remove,
>> +       .driver = {
>> +               .name = "geni_spi",
>> +               .pm = &spi_geni_pm_ops,
>> +               .of_match_table = spi_geni_dt_match,
>> +       },
>> +};
>> +module_platform_driver(spi_geni_driver);
>> +
>> +MODULE_DESCRIPTION("SPI driver for GENI based QUP cores");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/spi/spi-geni-qcom.h 
>> b/include/linux/spi/spi-geni-qcom.h
>> new file mode 100644
>> index 0000000..dc95764
>> --- /dev/null
>> +++ b/include/linux/spi/spi-geni-qcom.h
>> @@ -0,0 +1,14 @@
>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>> +/*
>> + * Copyright (c) 2017-2018, The Linux Foundation. All rights 
>> reserved.
>> + */
>> +
>> +#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;
> 
> It was decided we still needed this? I don't see it in v3 so please
> remove this file unless it's needed.
Its not required; I will remove it.

Along with these changes, i will submit the fixes of loopback issue and 
suspend/resume issue.

--Dilip
Doug Anderson Sept. 25, 2018, 7:54 p.m. UTC | #5
Hi,

On Tue, Sep 25, 2018 at 12:36 PM <dkota@codeaurora.org> wrote:
> >> +       unsigned int cur_speed_hz;
> >
> > unsigned long for Hz? The clk framework uses that type.
>
> cur_speed_hz stores the speed value requested as part of transfer (not
> the resultant or rounded off frequency got from clk framework. It is u32
> type, i will change cur_speed_hz to u32 type instead of unsigned long.
> Code snippet:
>          mas->cur_speed_hz = xfer->speed_hz;

Change it to "unsigned long" anyway to match the clock framework.  In
theory maybe the "xfer" structure will be updated eventually.


-Doug
Dilip Kota Sept. 26, 2018, 6:36 p.m. UTC | #6
On 2018-09-26 01:24, Doug Anderson wrote:
> Hi,
> 
> On Tue, Sep 25, 2018 at 12:36 PM <dkota@codeaurora.org> wrote:
>> >> +       unsigned int cur_speed_hz;
>> >
>> > unsigned long for Hz? The clk framework uses that type.
>> 
>> cur_speed_hz stores the speed value requested as part of transfer (not
>> the resultant or rounded off frequency got from clk framework. It is 
>> u32
>> type, i will change cur_speed_hz to u32 type instead of unsigned long.
>> Code snippet:
>>          mas->cur_speed_hz = xfer->speed_hz;
> 
> Change it to "unsigned long" anyway to match the clock framework.  In
> theory maybe the "xfer" structure will be updated eventually.
Ok. WIll update it
> 


>> Is it possible for all three conditions above to happen in one
>> interrupt? I ask because 'ret' is overwritten and so what may have 
>> been
>> IRQ_HANDLED may become IRQ_NONE which will lead to confusion in the 
>> irq
>> layer. Maybe the handle tx/rx functions can return a bool, that gets
>> orred together each time so that we know if something handled an
>> interrupt?
>> 
> Will check it again by running fullduplex transfer and update.
Added the changes in V5 to ensure return value doesn't get overwritten.

--Dilip
diff mbox series

Patch

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index ad5d68e..8934df8 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -533,6 +533,18 @@  config SPI_QUP
 	  This driver can also be built as a module.  If so, the module
 	  will be called spi_qup.
 
+config SPI_QCOM_GENI
+	tristate "Qualcomm GENI based SPI controller"
+	depends on QCOM_GENI_SE
+	help
+	  This driver supports GENI serial engine based SPI controller in
+	  master mode on the Qualcomm Technologies Inc.'s SoCs. If you say
+	  yes to this option, support will be included for the built-in SPI
+	  interface on the Qualcomm Technologies Inc.'s SoCs.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called spi-geni-qcom.
+
 config SPI_S3C24XX
 	tristate "Samsung S3C24XX series SPI"
 	depends on ARCH_S3C24XX
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index cb1f437..98337cf 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -74,6 +74,7 @@  obj-$(CONFIG_SPI_PPC4xx)		+= spi-ppc4xx.o
 spi-pxa2xx-platform-objs		:= spi-pxa2xx.o spi-pxa2xx-dma.o
 obj-$(CONFIG_SPI_PXA2XX)		+= spi-pxa2xx-platform.o
 obj-$(CONFIG_SPI_PXA2XX_PCI)		+= spi-pxa2xx-pci.o
+obj-$(CONFIG_SPI_QCOM_GENI)		+= spi-geni-qcom.o
 obj-$(CONFIG_SPI_QUP)			+= spi-qup.o
 obj-$(CONFIG_SPI_ROCKCHIP)		+= spi-rockchip.o
 obj-$(CONFIG_SPI_RB4XX)			+= spi-rb4xx.o
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
new file mode 100644
index 0000000..949b853
--- /dev/null
+++ b/drivers/spi/spi-geni-qcom.c
@@ -0,0 +1,653 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/qcom-geni-se.h>
+#include <linux/spi/spi.h>
+#include <linux/spinlock.h>
+
+/* SPI SE specific registers and respective register fields */
+#define SE_SPI_CPHA		0x224
+#define CPHA			BIT(0)
+
+#define SE_SPI_LOOPBACK		0x22c
+#define LOOPBACK_ENABLE		0x1
+#define NORMAL_MODE		0x0
+#define LOOPBACK_MSK		GENMASK(1, 0)
+
+#define SE_SPI_CPOL		0x230
+#define CPOL			BIT(2)
+
+#define SE_SPI_DEMUX_OUTPUT_INV	0x24c
+#define CS_DEMUX_OUTPUT_INV_MSK	GENMASK(3, 0)
+
+#define SE_SPI_DEMUX_SEL	0x250
+#define CS_DEMUX_OUTPUT_SEL	GENMASK(3, 0)
+
+#define SE_SPI_TRANS_CFG	0x25c
+#define CS_TOGGLE		BIT(0)
+
+#define SE_SPI_WORD_LEN		0x268
+#define WORD_LEN_MSK		GENMASK(9, 0)
+#define MIN_WORD_LEN		4
+
+#define SE_SPI_TX_TRANS_LEN	0x26c
+#define SE_SPI_RX_TRANS_LEN	0x270
+#define TRANS_LEN_MSK		GENMASK(23, 0)
+
+#define SE_SPI_PRE_POST_CMD_DLY	0x274
+
+#define SE_SPI_DELAY_COUNTERS	0x278
+#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
+#define SPI_FULL_DUPLEX		3
+#define SPI_TX_RX		7
+#define SPI_CS_ASSERT		8
+#define SPI_CS_DEASSERT		9
+#define SPI_SCK_ONLY		10
+/* M_CMD params for SPI */
+#define SPI_PRE_CMD_DELAY	BIT(0)
+#define TIMESTAMP_BEFORE	BIT(1)
+#define FRAGMENTATION		BIT(2)
+#define TIMESTAMP_AFTER		BIT(3)
+#define POST_CMD_DELAY		BIT(4)
+
+static irqreturn_t geni_spi_isr(int irq, void *data);
+
+struct spi_geni_master {
+	struct geni_se se;
+	struct device *dev;
+	u32 rx_fifo_depth;
+	u32 tx_fifo_depth;
+	u32 fifo_width_bits;
+	u32 tx_wm;
+	unsigned int cur_speed_hz;
+	unsigned int cur_bits_per_word;
+	unsigned int tx_rem_bytes;
+	unsigned int rx_rem_bytes;
+	struct spi_transfer *cur_xfer;
+	struct completion xfer_done;
+	unsigned int oversampling;
+	spinlock_t lock;
+};
+
+static int get_spi_clk_cfg(unsigned int speed_hz,
+			struct spi_geni_master *mas,
+			unsigned int *clk_idx,
+			unsigned int *clk_div)
+{
+	unsigned long sclk_freq;
+	unsigned int actual_hz;
+	struct geni_se *se = &mas->se;
+	int ret;
+
+	ret = geni_se_clk_freq_match(&mas->se,
+				speed_hz * mas->oversampling,
+				clk_idx, &sclk_freq, false);
+	if (ret) {
+		dev_err(mas->dev, "Failed(%d) to find src clk for %dHz\n",
+							ret, speed_hz);
+		return ret;
+	}
+
+	*clk_div = DIV_ROUND_UP(sclk_freq, mas->oversampling * speed_hz);
+	actual_hz = sclk_freq / (mas->oversampling * *clk_div);
+
+	dev_dbg(mas->dev, "req %u=>%u sclk %lu, idx %d, div %d\n", speed_hz,
+				actual_hz, sclk_freq, *clk_idx, *clk_div);
+	ret = clk_set_rate(se->clk, sclk_freq);
+	if (ret)
+		dev_err(mas->dev, "clk_set_rate failed %d\n", ret);
+	return ret;
+}
+
+static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode,
+					unsigned int bits_per_word)
+{
+	unsigned int pack_words;
+	bool msb_first = (mode & SPI_LSB_FIRST) ? false : true;
+	struct geni_se *se = &mas->se;
+	u32 word_len;
+
+	word_len = readl(se->base + SE_SPI_WORD_LEN);
+
+	/*
+	 * If bits_per_word isn't a byte aligned value, set the packing to be
+	 * 1 SPI word per FIFO word.
+	 */
+	if (!(mas->fifo_width_bits % bits_per_word))
+		pack_words = mas->fifo_width_bits / bits_per_word;
+	else
+		pack_words = 1;
+	word_len &= ~WORD_LEN_MSK;
+	word_len |= ((bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK);
+	geni_se_config_packing(&mas->se, bits_per_word, pack_words, msb_first,
+								true, true);
+	writel(word_len, se->base + SE_SPI_WORD_LEN);
+}
+
+static int setup_fifo_params(struct spi_device *spi_slv,
+					struct spi_master *spi)
+{
+	struct spi_geni_master *mas = spi_master_get_devdata(spi);
+	struct geni_se *se = &mas->se;
+	u32 loopback_cfg, cpol, cpha, demux_output_inv;
+	u32 demux_sel, clk_sel, m_clk_cfg, idx, div;
+	int ret;
+
+	loopback_cfg = readl(se->base + SE_SPI_LOOPBACK);
+	cpol = readl(se->base + SE_SPI_CPOL);
+	cpha = readl(se->base + SE_SPI_CPHA);
+	demux_output_inv = 0;
+	loopback_cfg &= ~LOOPBACK_MSK;
+	cpol &= ~CPOL;
+	cpha &= ~CPHA;
+
+	if (spi_slv->mode & SPI_LOOP)
+		loopback_cfg |= LOOPBACK_ENABLE;
+
+	if (spi_slv->mode & SPI_CPOL)
+		cpol |= CPOL;
+
+	if (spi_slv->mode & SPI_CPHA)
+		cpha |= CPHA;
+
+	if (spi_slv->mode & SPI_CS_HIGH)
+		demux_output_inv = BIT(spi_slv->chip_select);
+
+	demux_sel = spi_slv->chip_select;
+	mas->cur_speed_hz = spi_slv->max_speed_hz;
+	mas->cur_bits_per_word = spi_slv->bits_per_word;
+
+	ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, &idx, &div);
+	if (ret) {
+		dev_err(mas->dev, "Err setting clks ret(%d) for %d\n",
+							ret, mas->cur_speed_hz);
+		return ret;
+	}
+
+	clk_sel = idx & CLK_SEL_MSK;
+	m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
+	spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word);
+	writel(loopback_cfg, se->base + SE_SPI_LOOPBACK);
+	writel(demux_sel, se->base + SE_SPI_DEMUX_SEL);
+	writel(cpha, se->base + SE_SPI_CPHA);
+	writel(cpol, se->base + SE_SPI_CPOL);
+	writel(demux_output_inv, se->base + SE_SPI_DEMUX_OUTPUT_INV);
+	writel(clk_sel, se->base + SE_GENI_CLK_SEL);
+	writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG);
+	return 0;
+}
+
+static int spi_geni_prepare_message(struct spi_master *spi,
+					struct spi_message *spi_msg)
+{
+	int ret;
+	struct spi_geni_master *mas = spi_master_get_devdata(spi);
+	struct geni_se *se = &mas->se;
+
+	geni_se_select_mode(se, GENI_SE_FIFO);
+	reinit_completion(&mas->xfer_done);
+	ret = setup_fifo_params(spi_msg->spi, spi);
+	if (ret)
+		dev_err(mas->dev, "Couldn't select mode %d", ret);
+	return ret;
+}
+
+static int spi_geni_init(struct spi_geni_master *mas)
+{
+	struct geni_se *se = &mas->se;
+	unsigned int proto, major, minor, ver;
+
+	pm_runtime_get_sync(mas->dev);
+
+	proto = geni_se_read_proto(se);
+	if (proto != GENI_SE_SPI) {
+		dev_err(mas->dev, "Invalid proto %d\n", proto);
+		pm_runtime_put(mas->dev);
+		return -ENXIO;
+	}
+	mas->tx_fifo_depth = geni_se_get_tx_fifo_depth(se);
+	mas->rx_fifo_depth = geni_se_get_rx_fifo_depth(se);
+
+	/* Width of Tx and Rx FIFO is same */
+	mas->fifo_width_bits = geni_se_get_tx_fifo_width(se);
+
+	/*
+	 * Hardware programming guide suggests to configure
+	 * RX FIFO RFR level to fifo_depth-2.
+	 */
+	geni_se_init(se, 0x0, mas->tx_fifo_depth - 2);
+	/* Transmit an entire FIFO worth of data per IRQ */
+	mas->tx_wm = 1;
+	ver = geni_se_get_qup_hw_version(se);
+	major = GENI_SE_VERSION_MAJOR(ver);
+	minor = GENI_SE_VERSION_MINOR(ver);
+
+	if (major == 1 && minor == 0)
+		mas->oversampling = 2;
+	else
+		mas->oversampling = 1;
+
+	pm_runtime_put(mas->dev);
+	return 0;
+}
+
+static void setup_fifo_xfer(struct spi_transfer *xfer,
+				struct spi_geni_master *mas,
+				u16 mode, struct spi_master *spi)
+{
+	u32 m_cmd = 0, m_param = 0;
+	u32 spi_tx_cfg, trans_len;
+	struct geni_se *se = &mas->se;
+
+	spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG);
+	if (xfer->bits_per_word != mas->cur_bits_per_word) {
+		spi_setup_word_len(mas, mode, xfer->bits_per_word);
+		mas->cur_bits_per_word = xfer->bits_per_word;
+	}
+
+	/* Speed and bits per word can be overridden per transfer */
+	if (xfer->speed_hz != mas->cur_speed_hz) {
+		int ret;
+		u32 clk_sel, m_clk_cfg;
+		unsigned int idx, div;
+
+		ret = get_spi_clk_cfg(xfer->speed_hz, mas, &idx, &div);
+		if (ret) {
+			dev_err(mas->dev, "Err setting clks:%d\n", ret);
+			return;
+		}
+		/*
+		 * SPI core clock gets configured with the requested frequency
+		 * or the frequency closer to the requested frequency.
+		 * For that reason requested frequency is stored in the
+		 * cur_speed_hz and referred in the consicutive transfer instead
+		 * of calling clk_get_rate() API.
+		 */
+		mas->cur_speed_hz = xfer->speed_hz;
+		clk_sel = idx & CLK_SEL_MSK;
+		m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
+		writel(clk_sel, se->base + SE_GENI_CLK_SEL);
+		writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG);
+	}
+
+	mas->tx_rem_bytes = 0;
+	mas->rx_rem_bytes = 0;
+	if (xfer->tx_buf && xfer->rx_buf)
+		m_cmd = SPI_FULL_DUPLEX;
+	else if (xfer->tx_buf)
+		m_cmd = SPI_TX_ONLY;
+	else if (xfer->rx_buf)
+		m_cmd = SPI_RX_ONLY;
+
+	spi_tx_cfg &= ~CS_TOGGLE;
+	if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) {
+		trans_len =
+			(xfer->len * BITS_PER_BYTE /
+					mas->cur_bits_per_word) & TRANS_LEN_MSK;
+	} else {
+		unsigned int bytes_per_word =
+			mas->cur_bits_per_word / BITS_PER_BYTE + 1;
+
+		trans_len = (xfer->len / bytes_per_word) & TRANS_LEN_MSK;
+	}
+
+	/*
+	 * If CS change flag is set, then toggle the CS line in between
+	 * transfers and keep CS asserted after the last transfer.
+	 * Else if keep CS flag asserted in between transfers and de-assert
+	 * CS after the last message.
+	 */
+	if (xfer->cs_change) {
+		if (list_is_last(&xfer->transfer_list,
+				&spi->cur_msg->transfers))
+			m_param = FRAGMENTATION;
+	} else {
+		if (!list_is_last(&xfer->transfer_list,
+				&spi->cur_msg->transfers))
+			m_param = FRAGMENTATION;
+	}
+
+	mas->cur_xfer = xfer;
+	if (m_cmd & SPI_TX_ONLY) {
+		mas->tx_rem_bytes = xfer->len;
+		writel(trans_len, se->base + SE_SPI_TX_TRANS_LEN);
+	}
+
+	if (m_cmd & SPI_RX_ONLY) {
+		writel(trans_len, se->base + SE_SPI_RX_TRANS_LEN);
+		mas->rx_rem_bytes = xfer->len;
+	}
+	writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG);
+	geni_se_setup_m_cmd(se, m_cmd, m_param);
+
+	/*
+	 * TX_WATERMARK_REG should be set after SPI configuration and
+	 * setting up GENI SE engine, as driver starts data transfer
+	 * for the watermark interrupt.
+	 */
+	if (m_cmd & SPI_TX_ONLY)
+		writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG);
+}
+
+static void handle_fifo_timeout(struct spi_master *spi,
+				struct spi_message *msg)
+{
+	struct spi_geni_master *mas = spi_master_get_devdata(spi);
+	unsigned long timeout, flags;
+	struct geni_se *se = &mas->se;
+
+	spin_lock_irqsave(&mas->lock, flags);
+	reinit_completion(&mas->xfer_done);
+	geni_se_cancel_m_cmd(se);
+	writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
+	spin_unlock_irqrestore(&mas->lock, flags);
+	timeout = wait_for_completion_timeout(&mas->xfer_done, HZ);
+	if (!timeout) {
+		spin_lock_irqsave(&mas->lock, flags);
+		reinit_completion(&mas->xfer_done);
+		geni_se_abort_m_cmd(se);
+		spin_unlock_irqrestore(&mas->lock, flags);
+		timeout = wait_for_completion_timeout(&mas->xfer_done,
+								HZ);
+		if (!timeout)
+			dev_err(mas->dev,
+				"Failed to cancel/abort m_cmd\n");
+	}
+}
+
+static int spi_geni_transfer_one(struct spi_master *spi,
+				struct spi_device *slv,
+				struct spi_transfer *xfer)
+{
+	struct spi_geni_master *mas = spi_master_get_devdata(spi);
+
+	setup_fifo_xfer(xfer, mas, slv->mode, spi);
+	return 1;
+}
+
+static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas)
+{
+	/*
+	 * Calculate how many bytes we'll put in each FIFO word.  If the
+	 * transfer words don't pack cleanly into a FIFO word we'll just put
+	 * one transfer word in each FIFO word.  If they do pack we'll pack 'em.
+	 */
+	if (mas->fifo_width_bits % mas->cur_bits_per_word)
+		return roundup_pow_of_two(DIV_ROUND_UP(mas->cur_bits_per_word,
+						       BITS_PER_BYTE));
+	else
+		return mas->fifo_width_bits / BITS_PER_BYTE;
+}
+
+static irqreturn_t geni_spi_handle_tx(struct spi_geni_master *mas)
+{
+	struct geni_se *se = &mas->se;
+	unsigned int max_bytes;
+	const u8 *tx_buf;
+	unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas);
+	unsigned int i = 0;
+
+	if (!mas->cur_xfer)
+		return IRQ_NONE;
+
+	max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * bytes_per_fifo_word;
+	if (mas->tx_rem_bytes < max_bytes)
+		max_bytes = mas->tx_rem_bytes;
+
+	tx_buf = mas->cur_xfer->tx_buf + mas->cur_xfer->len - mas->tx_rem_bytes;
+	while (i < max_bytes) {
+		unsigned int j;
+		unsigned int bytes_to_write;
+		u32 fifo_word = 0;
+		u8 *fifo_byte = (u8 *)&fifo_word;
+
+		bytes_to_write = min(bytes_per_fifo_word, max_bytes - i);
+		for (j = 0; j < bytes_to_write; j++)
+			fifo_byte[j] = tx_buf[i++];
+		iowrite32_rep(se->base + SE_GENI_TX_FIFOn, &fifo_word, 1);
+	}
+	mas->tx_rem_bytes -= max_bytes;
+	if (!mas->tx_rem_bytes)
+		writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t geni_spi_handle_rx(struct spi_geni_master *mas)
+{
+	struct geni_se *se = &mas->se;
+	u32 rx_fifo_status;
+	unsigned int rx_bytes;
+	u8 *rx_buf;
+	unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas);
+	unsigned int i = 0;
+
+	if (!mas->cur_xfer)
+		return IRQ_NONE;
+
+	rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS);
+	rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * bytes_per_fifo_word;
+	if (rx_fifo_status & RX_LAST) {
+		unsigned int rx_last_byte_valid =
+			(rx_fifo_status & RX_LAST_BYTE_VALID_MSK)
+				>> RX_LAST_BYTE_VALID_SHFT;
+		if (rx_last_byte_valid && (rx_last_byte_valid < 4))
+			rx_bytes -= bytes_per_fifo_word - rx_last_byte_valid;
+	}
+	if (mas->rx_rem_bytes < rx_bytes)
+		rx_bytes = mas->rx_rem_bytes;
+
+	rx_buf = mas->cur_xfer->rx_buf + mas->cur_xfer->len - mas->rx_rem_bytes;
+	while (i < rx_bytes) {
+		u32 fifo_word = 0;
+		u8 *fifo_byte = (u8 *)&fifo_word;
+		unsigned int bytes_to_read;
+		unsigned int j;
+
+		bytes_to_read = min(bytes_per_fifo_word, rx_bytes - i);
+		ioread32_rep(se->base + SE_GENI_RX_FIFOn, &fifo_word, 1);
+		for (j = 0; j < bytes_to_read; j++)
+			rx_buf[i++] = fifo_byte[j];
+	}
+	mas->rx_rem_bytes -= rx_bytes;
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t geni_spi_isr(int irq, void *data)
+{
+	struct spi_master *spi = data;
+	struct spi_geni_master *mas = spi_master_get_devdata(spi);
+	struct geni_se *se = &mas->se;
+	u32 m_irq;
+	unsigned long flags;
+	irqreturn_t ret = IRQ_HANDLED;
+
+	if (pm_runtime_status_suspended(mas->dev))
+		return IRQ_NONE;
+
+	spin_lock_irqsave(&mas->lock, flags);
+	m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
+	if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN))
+		ret = geni_spi_handle_rx(mas);
+
+	if (m_irq & M_TX_FIFO_WATERMARK_EN)
+		ret = geni_spi_handle_tx(mas);
+
+	if (m_irq & M_CMD_DONE_EN) {
+		spi_finalize_current_transfer(spi);
+		/*
+		 * If this happens, then a CMD_DONE came before all the Tx
+		 * buffer bytes were sent out. This is unusual, log this
+		 * condition and disable the WM interrupt to prevent the
+		 * system from stalling due an interrupt storm.
+		 * If this happens when all Rx bytes haven't been received, log
+		 * the condition.
+		 * The only known time this can happen is if bits_per_word != 8
+		 * and some registers that expect xfer lengths in num spi_words
+		 * weren't written correctly.
+		 */
+		if (mas->tx_rem_bytes) {
+			writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
+			dev_err(mas->dev, "Premature Done.tx_rem%d bpw%d\n",
+				mas->tx_rem_bytes, mas->cur_bits_per_word);
+		}
+		if (mas->rx_rem_bytes)
+			dev_err(mas->dev, "Premature Done.rx_rem%d bpw%d\n",
+				mas->rx_rem_bytes, mas->cur_bits_per_word);
+	}
+
+	if ((m_irq & M_CMD_CANCEL_EN) || (m_irq & M_CMD_ABORT_EN))
+		complete(&mas->xfer_done);
+
+	writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR);
+	spin_unlock_irqrestore(&mas->lock, flags);
+	return ret;
+}
+
+static int spi_geni_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct spi_master *spi;
+	struct spi_geni_master *mas;
+	struct resource *res;
+	struct geni_se *se;
+
+	spi = spi_alloc_master(&pdev->dev, sizeof(struct spi_geni_master));
+	if (!spi)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, spi);
+	mas = spi_master_get_devdata(spi);
+	mas->dev = &pdev->dev;
+	mas->se.dev = &pdev->dev;
+	mas->se.wrapper = dev_get_drvdata(pdev->dev.parent);
+	se = &mas->se;
+
+	spi->bus_num = -1;
+	spi->dev.of_node = pdev->dev.of_node;
+	mas->se.clk = devm_clk_get(&pdev->dev, "se");
+	if (IS_ERR(mas->se.clk)) {
+		ret = PTR_ERR(mas->se.clk);
+		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
+		goto spi_geni_probe_err;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	se->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(se->base)) {
+		ret = -ENOMEM;
+		goto spi_geni_probe_err;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Err getting IRQ %d\n", ret);
+		goto spi_geni_probe_err;
+	}
+	ret = devm_request_irq(&pdev->dev, ret, geni_spi_isr,
+				IRQF_TRIGGER_HIGH, "spi_geni", spi);
+	if (ret)
+		goto spi_geni_probe_err;
+
+	spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
+	spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
+	spi->num_chipselect = 4;
+	spi->max_speed_hz = 50000000;
+	spi->prepare_message = spi_geni_prepare_message;
+	spi->transfer_one = spi_geni_transfer_one;
+	spi->auto_runtime_pm = true;
+	spi->handle_err = handle_fifo_timeout;
+
+	init_completion(&mas->xfer_done);
+	spin_lock_init(&mas->lock);
+	pm_runtime_enable(&pdev->dev);
+
+	ret = spi_geni_init(mas);
+	if (ret)
+		goto spi_geni_probe_runtime_disable;
+
+	ret = spi_register_master(spi);
+	if (ret)
+		goto spi_geni_probe_runtime_disable;
+
+	return 0;
+spi_geni_probe_runtime_disable:
+	pm_runtime_disable(&pdev->dev);
+spi_geni_probe_err:
+	spi_master_put(spi);
+	return ret;
+}
+
+static int spi_geni_remove(struct platform_device *pdev)
+{
+	struct spi_master *spi = platform_get_drvdata(pdev);
+
+	/* Unregister _before_ disabling pm_runtime() so we stop transfers */
+	spi_unregister_master(spi);
+
+	pm_runtime_disable(&pdev->dev);
+	return 0;
+}
+
+static int __maybe_unused spi_geni_runtime_suspend(struct device *dev)
+{
+	struct spi_master *spi = dev_get_drvdata(dev);
+	struct spi_geni_master *mas = spi_master_get_devdata(spi);
+
+	return geni_se_resources_off(&mas->se);
+}
+
+static int __maybe_unused spi_geni_runtime_resume(struct device *dev)
+{
+	struct spi_master *spi = dev_get_drvdata(dev);
+	struct spi_geni_master *mas = spi_master_get_devdata(spi);
+
+	return geni_se_resources_on(&mas->se);
+}
+
+static int __maybe_unused spi_geni_suspend(struct device *dev)
+{
+	if (!pm_runtime_status_suspended(dev))
+		return -EBUSY;
+	return 0;
+}
+
+static const struct dev_pm_ops spi_geni_pm_ops = {
+	SET_RUNTIME_PM_OPS(spi_geni_runtime_suspend,
+					spi_geni_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(spi_geni_suspend, NULL)
+};
+
+static const struct of_device_id spi_geni_dt_match[] = {
+	{ .compatible = "qcom,geni-spi" },
+	{}
+};
+
+static struct platform_driver spi_geni_driver = {
+	.probe  = spi_geni_probe,
+	.remove = spi_geni_remove,
+	.driver = {
+		.name = "geni_spi",
+		.pm = &spi_geni_pm_ops,
+		.of_match_table = spi_geni_dt_match,
+	},
+};
+module_platform_driver(spi_geni_driver);
+
+MODULE_DESCRIPTION("SPI driver for GENI based QUP cores");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/spi/spi-geni-qcom.h b/include/linux/spi/spi-geni-qcom.h
new file mode 100644
index 0000000..dc95764
--- /dev/null
+++ b/include/linux/spi/spi-geni-qcom.h
@@ -0,0 +1,14 @@ 
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ */
+
+#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___*/