spi: sh-msiof: Add slave mode support
authorHisashi Nakamura <hisashi.nakamura.ak@renesas.com>
Mon, 22 May 2017 13:11:43 +0000 (15:11 +0200)
committerMark Brown <broonie@kernel.org>
Fri, 26 May 2017 12:11:54 +0000 (13:11 +0100)
Add slave mode support to the MSIOF driver, in both PIO and DMA mode.

For now this only supports the transmission of messages with a size
that is known in advance.

Signed-off-by: Hisashi Nakamura <hisashi.nakamura.ak@renesas.com>
Signed-off-by: Hiromitsu Yamasaki <hiromitsu.yamasaki.ym@renesas.com>
[geert: Timeout handling cleanup, spi core integration, cancellation,
rewording]
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
Documentation/devicetree/bindings/spi/sh-msiof.txt
drivers/spi/spi-sh-msiof.c
include/linux/spi/sh_msiof.h

index dc97506..64ee489 100644 (file)
@@ -38,6 +38,8 @@ Optional properties:
                         specifiers, one for transmission, and one for
                         reception.
 - dma-names            : Must contain a list of two DMA names, "tx" and "rx".
+- spi-slave            : Empty property indicating the SPI controller is used
+                        in slave mode.
 - renesas,dtdl         : delay sync signal (setup) in transmit mode.
                         Must contain one of the following values:
                         0   (no bit delay)
index 2ce15ca..c304c71 100644 (file)
@@ -2,7 +2,8 @@
  * SuperH MSIOF SPI Master Interface
  *
  * Copyright (c) 2009 Magnus Damm
- * Copyright (C) 2014 Glider bvba
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ * Copyright (C) 2014-2017 Glider bvba
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -33,7 +34,6 @@
 
 #include <asm/unaligned.h>
 
-
 struct sh_msiof_chipdata {
        u16 tx_fifo_size;
        u16 rx_fifo_size;
@@ -53,6 +53,7 @@ struct sh_msiof_spi_priv {
        void *rx_dma_page;
        dma_addr_t tx_dma_addr;
        dma_addr_t rx_dma_addr;
+       bool slave_aborted;
 };
 
 #define TMDR1  0x00    /* Transmit Mode Register 1 */
@@ -337,7 +338,10 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
        tmp |= !cs_high << MDR1_SYNCAC_SHIFT;
        tmp |= lsb_first << MDR1_BITLSB_SHIFT;
        tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p);
-       sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
+       if (spi_controller_is_slave(p->master))
+               sh_msiof_write(p, TMDR1, tmp | TMDR1_PCON);
+       else
+               sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
        if (p->master->flags & SPI_MASTER_MUST_TX) {
                /* These bits are reserved if RX needs TX */
                tmp &= ~0x0000ffff;
@@ -564,17 +568,19 @@ static int sh_msiof_prepare_message(struct spi_master *master,
 
 static int sh_msiof_spi_start(struct sh_msiof_spi_priv *p, void *rx_buf)
 {
-       int ret;
+       bool slave = spi_controller_is_slave(p->master);
+       int ret = 0;
 
        /* setup clock and rx/tx signals */
-       ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
+       if (!slave)
+               ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
        if (rx_buf && !ret)
                ret = sh_msiof_modify_ctr_wait(p, 0, CTR_RXE);
        if (!ret)
                ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
 
        /* start by setting frame bit */
-       if (!ret)
+       if (!ret && !slave)
                ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
 
        return ret;
@@ -582,20 +588,49 @@ static int sh_msiof_spi_start(struct sh_msiof_spi_priv *p, void *rx_buf)
 
 static int sh_msiof_spi_stop(struct sh_msiof_spi_priv *p, void *rx_buf)
 {
-       int ret;
+       bool slave = spi_controller_is_slave(p->master);
+       int ret = 0;
 
        /* shut down frame, rx/tx and clock signals */
-       ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
+       if (!slave)
+               ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
        if (!ret)
                ret = sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
        if (rx_buf && !ret)
                ret = sh_msiof_modify_ctr_wait(p, CTR_RXE, 0);
-       if (!ret)
+       if (!ret && !slave)
                ret = sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0);
 
        return ret;
 }
 
+static int sh_msiof_slave_abort(struct spi_master *master)
+{
+       struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
+
+       p->slave_aborted = true;
+       complete(&p->done);
+       return 0;
+}
+
+static int sh_msiof_wait_for_completion(struct sh_msiof_spi_priv *p)
+{
+       if (spi_controller_is_slave(p->master)) {
+               if (wait_for_completion_interruptible(&p->done) ||
+                   p->slave_aborted) {
+                       dev_dbg(&p->pdev->dev, "interrupted\n");
+                       return -EINTR;
+               }
+       } else {
+               if (!wait_for_completion_timeout(&p->done, HZ)) {
+                       dev_err(&p->pdev->dev, "timeout\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
+       return 0;
+}
+
 static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
                                  void (*tx_fifo)(struct sh_msiof_spi_priv *,
                                                  const void *, int, int),
@@ -628,6 +663,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
                tx_fifo(p, tx_buf, words, fifo_shift);
 
        reinit_completion(&p->done);
+       p->slave_aborted = false;
 
        ret = sh_msiof_spi_start(p, rx_buf);
        if (ret) {
@@ -636,11 +672,9 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
        }
 
        /* wait for tx fifo to be emptied / rx fifo to be filled */
-       if (!wait_for_completion_timeout(&p->done, HZ)) {
-               dev_err(&p->pdev->dev, "PIO timeout\n");
-               ret = -ETIMEDOUT;
+       ret = sh_msiof_wait_for_completion(p);
+       if (ret)
                goto stop_reset;
-       }
 
        /* read rx fifo */
        if (rx_buf)
@@ -732,6 +766,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
        sh_msiof_write(p, IER, ier_bits);
 
        reinit_completion(&p->done);
+       p->slave_aborted = false;
 
        /* Now start DMA */
        if (rx)
@@ -746,11 +781,9 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
        }
 
        /* wait for tx fifo to be emptied / rx fifo to be filled */
-       if (!wait_for_completion_timeout(&p->done, HZ)) {
-               dev_err(&p->pdev->dev, "DMA timeout\n");
-               ret = -ETIMEDOUT;
+       ret = sh_msiof_wait_for_completion(p);
+       if (ret)
                goto stop_reset;
-       }
 
        /* clear status bits */
        sh_msiof_reset_str(p);
@@ -843,7 +876,8 @@ static int sh_msiof_transfer_one(struct spi_master *master,
        int ret;
 
        /* setup clocks (clock already enabled in chipselect()) */
-       sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
+       if (!spi_controller_is_slave(p->master))
+               sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
 
        while (master->dma_tx && len > 15) {
                /*
@@ -998,8 +1032,12 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
        if (!info)
                return NULL;
 
+       info->mode = of_property_read_bool(np, "spi-slave") ? MSIOF_SPI_SLAVE
+                                                           : MSIOF_SPI_MASTER;
+
        /* Parse the MSIOF properties */
-       of_property_read_u32(np, "num-cs", &num_cs);
+       if (info->mode == MSIOF_SPI_MASTER)
+               of_property_read_u32(np, "num-cs", &num_cs);
        of_property_read_u32(np, "renesas,tx-fifo-size",
                                        &info->tx_fifo_override);
        of_property_read_u32(np, "renesas,rx-fifo-size",
@@ -1159,34 +1197,40 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
        struct spi_master *master;
        const struct sh_msiof_chipdata *chipdata;
        const struct of_device_id *of_id;
+       struct sh_msiof_spi_info *info;
        struct sh_msiof_spi_priv *p;
        int i;
        int ret;
 
-       master = spi_alloc_master(&pdev->dev, sizeof(struct sh_msiof_spi_priv));
-       if (master == NULL)
-               return -ENOMEM;
-
-       p = spi_master_get_devdata(master);
-
-       platform_set_drvdata(pdev, p);
-       p->master = master;
-
        of_id = of_match_device(sh_msiof_match, &pdev->dev);
        if (of_id) {
                chipdata = of_id->data;
-               p->info = sh_msiof_spi_parse_dt(&pdev->dev);
+               info = sh_msiof_spi_parse_dt(&pdev->dev);
        } else {
                chipdata = (const void *)pdev->id_entry->driver_data;
-               p->info = dev_get_platdata(&pdev->dev);
+               info = dev_get_platdata(&pdev->dev);
        }
 
-       if (!p->info) {
+       if (!info) {
                dev_err(&pdev->dev, "failed to obtain device info\n");
-               ret = -ENXIO;
-               goto err1;
+               return -ENXIO;
        }
 
+       if (info->mode == MSIOF_SPI_SLAVE)
+               master = spi_alloc_slave(&pdev->dev,
+                                        sizeof(struct sh_msiof_spi_priv));
+       else
+               master = spi_alloc_master(&pdev->dev,
+                                         sizeof(struct sh_msiof_spi_priv));
+       if (master == NULL)
+               return -ENOMEM;
+
+       p = spi_master_get_devdata(master);
+
+       platform_set_drvdata(pdev, p);
+       p->master = master;
+       p->info = info;
+
        init_completion(&p->done);
 
        p->clk = devm_clk_get(&pdev->dev, NULL);
@@ -1237,6 +1281,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
        master->num_chipselect = p->info->num_chipselect;
        master->setup = sh_msiof_spi_setup;
        master->prepare_message = sh_msiof_prepare_message;
+       master->slave_abort = sh_msiof_slave_abort;
        master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
        master->auto_runtime_pm = true;
        master->transfer_one = sh_msiof_transfer_one;
index b087a85..f74b581 100644 (file)
@@ -1,10 +1,16 @@
 #ifndef __SPI_SH_MSIOF_H__
 #define __SPI_SH_MSIOF_H__
 
+enum {
+       MSIOF_SPI_MASTER,
+       MSIOF_SPI_SLAVE,
+};
+
 struct sh_msiof_spi_info {
        int tx_fifo_override;
        int rx_fifo_override;
        u16 num_chipselect;
+       int mode;
        unsigned int dma_tx_id;
        unsigned int dma_rx_id;
        u32 dtdl;